diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 0c0dff6..31d90ae 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -1,14 +1,41 @@
name: Continuous Integration
+
on:
pull_request:
branches:
- main
+
jobs:
- build:
- name: Build
+ build-docker:
+ name: Build Docker
runs-on: ubuntu-latest
steps:
- name: Checkout repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
+
- name: Build service
run: docker build .
+
+ build-lint-test:
+ name: Build, Lint, and Test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version-file: package.json
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build
+ run: npm run build
+
+ - name: Lint
+ run: npm run lint
+
+ - name: Test
+ run: npm run test:run
diff --git a/.github/workflows/release-please-config.json b/.github/workflows/release-please-config.json
new file mode 100644
index 0000000..9c02db9
--- /dev/null
+++ b/.github/workflows/release-please-config.json
@@ -0,0 +1,8 @@
+{
+ "packages": {
+ ".": {
+ "release-type": "simple",
+ "package-name": "zap2xml"
+ }
+ }
+}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index d3d04ec..9c05102 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -14,13 +14,10 @@ jobs:
uses: actions/checkout@v4
- name: Setup release please
- uses: google-github-actions/release-please-action@v2
+ uses: googleapis/release-please-action@v4
id: release
with:
- token: ${{ secrets.GITHUB_TOKEN }}
- release-type: simple
- changelog-path: CHANGELOG.md
- package-name: zap2xml
+ config-file: .github/workflows/release-please-config.json
- name: Login into GitHub Container Registry
if: ${{ steps.release.outputs.release_created }}
diff --git a/.gitignore b/.gitignore
index fade825..dd2d7eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
-.zap2xmlrc
+build/
+node_modules/
-config/
-xmltv/
+compose.yaml
+
+xmltv.xml
diff --git a/.zap2xmlrc-example b/.zap2xmlrc-example
deleted file mode 100644
index 6263e8e..0000000
--- a/.zap2xmlrc-example
+++ /dev/null
@@ -1,57 +0,0 @@
-# Basic settings
-# start: Number of days to offset from today for the start date (default: 0, cmd: -s)
-start=0
-# days: Number of days of program data to fetch (default: 7, cmd: -d)
-days=7
-# retries: Number of connection retries before failure (default: 3, max: 20, cmd: -r)
-retries=3
-
-# Authentication
-# user: Username/email for Zap2it account (default: empty, cmd: -u)
-user=myemail@example.com
-# pass: Password for Zap2it account (default: empty, cmd: -p)
-pass=mypassword
-
-# Cache settings
-# cache: Directory to store cached data files (default: cache, cmd: -c)
-cache=/config/cache
-# ncdays: Number of days from the end to not cache (default: 0, cmd: -n)
-ncdays=0
-# ncsdays: Number of days from the start to not cache (default: 0, cmd: -N)
-ncsdays=0
-# ncmday: Specific day number to not cache, 1-based relative to start (default: -1, cmd: -B)
-ncmday=-1
-
-# Output settings
-# outfile: Output XML file path (default: xmltv.xml or xtvd.xml, cmd: -o)
-outfile=/xmltv/xmltv.xml
-# outformat: Output format - xmltv or xtvd (default: xmltv, cmd: -x forces xtvd)
-outformat=xmltv
-
-# Language
-# lang: Language code for program data (default: en, cmd: -l)
-lang=en
-
-# Media directories
-# icon: Directory to store channel icons (default: disabled, cmd: -i)
-icon=/config/icons
-# trailer: Directory to store movie trailers (default: disabled, cmd: -t)
-trailer=/config/trailers
-
-# Network
-# proxy: HTTP proxy server URL (default: none, cmd: -P)
-proxy=http://localhost:8080
-
-# XTVD format settings (only used when outformat=xtvd)
-# lineuptype: Type of lineup - Cable/CableDigital/Satellite/LocalBroadcast (default: none)
-lineuptype=Cable
-# lineupname: Name of the lineup (default: none)
-lineupname=My Cable Provider
-# lineuplocation: Location of the lineup (default: none)
-lineuplocation=New York, NY
-
-# Alternative authentication (TV Guide)
-# lineupid: Lineup ID for TV Guide, alternative to username/password (default: none, cmd: -Y)
-lineupid=X:80000
-# postalcode: Postal code for TV Guide lineup lookup (default: none, cmd: -Z)
-postalcode=01010
diff --git a/Dockerfile b/Dockerfile
index 6871720..cc30967 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,19 +1,18 @@
-FROM alpine:3.13.5
+FROM node:22.17.1-alpine3.22
-ENV SLEEPTIME=43200
+WORKDIR /app
-RUN apk add --no-cache \
- perl \
- perl-http-cookies \
- perl-lwp-useragent-determined \
- perl-json \
- perl-json-xs \
- perl-lwp-protocol-https \
- perl-gd
+COPY package.json package.json
+COPY package-lock.json package-lock.json
-WORKDIR /opt
+RUN npm ci
-COPY zap2xml.pl zap2xml.pl
+COPY tsconfig.json tsconfig.json
COPY entrypoint.sh entrypoint.sh
+COPY src/ src/
-ENTRYPOINT ["./entrypoint.sh"]
+RUN npm run build
+
+RUN ls -l /app
+
+ENTRYPOINT ["/bin/sh", "-c", "/app/entrypoint.sh"]
diff --git a/README.md b/README.md
index 57ab0fc..441f4d0 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,30 @@
# zap2xml
-See [zap2xml](https://web.archive.org/web/20200426004001/zap2xml.awardspace.info/) for original Perl script and guidance for the configuration file.
+See [zap2xml](https://web.archive.org/web/20200426004001/zap2xml.awardspace.info/) for original Perl script and guidance
+for the configuration file.
-## Docker
+## How to use
+
+### Retrieving your Lineup ID
+
+Visit the [Retrieving Lineup ID](https://github.com/jef/zap2xml/wiki/Retrieving-Lineup-ID) in the Wiki.
+
+### Node.js
+
+```bash
+npm i && npm run dev
+```
+
+See [Command line arguments](#command-line-arguments) for configuration options.
+
+### Docker
| Tag | Description |
| ------- | ----------------------- |
| latest | Stable zap2xml releases |
| nightly | HEAD zap2xml release |
-### docker-compose (recommended)
+#### docker-compose
```yaml
services:
@@ -17,74 +32,37 @@ services:
container_name: zap2xml
image: ghcr.io/jef/zap2xml:latest
environment:
- OPT_ARGS: >-
- -I -D -C /config/.zap2xmlrc -o /xmltv/xmltv.xml
- TZ: America/New_York # Consider using your timezone
+ OUTPUT_FILE: /xmltv/xmltv.xml
volumes:
- - /path/to/zap2xml/config:/config
- - /path/to/xmltv:/xmltv # nice for mapping other drives to this that may use xmltv.xml
+ - ./xmltv:/xmltv
restart: unless-stopped
```
+See [Environment variables](#environment-variables) for configuration options.
+
## Configuration
-### Optional environment variables
+### Environment variables
-| Variable | Description | Type | Default |
-| ------------ | ---------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
-| `USER_AGENT` | Custom user agent string for HTTP requests. | String | `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36` |
-| `SLEEPTIME` | Number of seconds to sleep between runs (useful for scheduling in Docker or cron). | Integer | `43200` |
-| `TZ` | Timezone for program times (affects output XML and Perl's time calculations). | String | System default |
+| Variable | Description | Type | Default |
+| ------------- | --------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------- |
+| `LINEUP_ID` | Lineup ID; You can find this at https://tvlistings.gracenote.com/grid-affiliates.html?aid=orbebb | String | `USA-lineupId-DEFAULT` (Attenna) |
+| `TIMESPAN` | Either 3 or 6 hours of shows | Integer | 3 |
+| `PREF` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | String | (empty) |
+| `POSTAL_CODE` | Postal code of where shows are available. | Integer | 30309 |
+| `USER_AGENT` | Custom user agent string for HTTP requests. | String | Uses random if not specified |
+| `TZ` | Timezone | String | System default |
+| `SLEEP_TIME` | Sleep time before next run in seconds (default: 10800, Only used with Docker.) | Integer | 10800 |
+| `OUTPUT_FILE` | Output file name (default: xmltv.xml) | String | xmltv.xml |
-### Optional run configurations
+### Command line arguments
-| Option | Type | Default | Description | Config File | Command Line |
-| ---------------- | --------- | ----------- | ------------------------------------------------------ | ----------------------------- | ------------ |
-| `start` | Integer | `0` | Number of days to offset from today for the start date | `start=1` | `-s` |
-| `days` | Integer | `7` | Number of days of program data to fetch | `days=14` | `-d` |
-| `retries` | Integer | `3` | Number of connection retries before failure (max 20) | `retries=5` | `-r` |
-| `user` | String | (empty) | Username/email for Zap2it account | `user=myemail@example.com` | `-u` |
-| `pass` | String | (empty) | Password for Zap2it account | `pass=mypassword` | `-p` |
-| `cache` | Directory | `cache` | Directory to store cached data files | `cache=/config/cache` | `-c` |
-| `ncdays` | Integer | `0` | Number of days from the end to not cache | `ncdays=2` | `-n` |
-| `ncsdays` | Integer | `0` | Number of days from the start to not cache | `ncsdays=1` | `-N` |
-| `ncmday` | Integer | `-1` | Specific day number to not cache (1-based) | `ncmday=3` | `-B` |
-| `outfile` | File path | `xmltv.xml` | Output XML file path | `outfile=/xmltv/xmltv.xml` | `-o` |
-| `outformat` | String | `xmltv` | Output format (xmltv/xtvd) | `outformat=xtvd` | `-x` |
-| `lang` | String | `en` | Language code for program data | `lang=es` | `-l` |
-| `icon` | Directory | (disabled) | Directory to store channel icons | `icon=/config/icons` | `-i` |
-| `trailer` | Directory | (disabled) | Directory to store movie trailers | `trailer=/config/trailers` | `-t` |
-| `proxy` | URL | (none) | HTTP proxy server URL | `proxy=http://localhost:8080` | `-P` |
-| `lineuptype` | String | (none) | Type of lineup (XTVD only) | `lineuptype=Cable` | - |
-| `lineupname` | String | (none) | Name of the lineup (XTVD only) | `lineupname=My Provider` | - |
-| `lineuplocation` | String | (none) | Location of the lineup (XTVD only) | `lineuplocation=New York, NY` | - |
-| `lineupid` | String | (none) | Lineup ID for TV Guide | `lineupid=X:80000` | `-Y` |
-| `postalcode` | String | (none) | Postal code for TV Guide | `postalcode=01010` | `-Z` |
-| `shiftMinutes` | Integer | `0` | Offset program times by minutes | - | `-m` |
-| `sleeptime` | Integer | `0` | Sleep between requests (seconds) | - | `-S` |
-| `allChan` | Boolean | `false` | Output all channels (not just favorites) | - | `-a` |
-| `outputXTVD` | Boolean | `false` | Force XTVD output format | - | `-x` |
-| `includeDetails` | Boolean | `false` | Include program details (extra requests) | - | `-D` |
-| `includeIcons` | Boolean | `false` | Include program icons (extra requests) | - | `-I` |
-| `retainOrder` | Boolean | `false` | Retain website channel order | - | `-b` |
-| `quiet` | Boolean | `false` | Quiet mode (no status output) | - | `-q` |
-| `wait` | Boolean | `false` | Wait on exit (require keypress) | - | `-w` |
-| `hexEncode` | Boolean | `false` | Hex encode HTML entities | - | `-e` |
-| `utf8` | Boolean | `false` | UTF-8 encoding (default: ISO-8859-1) | - | `-U` |
-| `liveTag` | Boolean | `false` | Output `` tag | - | `-L` |
-| `noTBA` | Boolean | `false` | Don't cache files with "TBA" titles | - | `-T` |
-| `channelFirst` | Boolean | `false` | Output channel names first | - | `-F` |
-| `oldStyle` | Boolean | `false` | Use old tv_grab_na style channel IDs | - | `-O` |
-| `appendFlags` | String | (none) | Append flags to program titles | - | `-A` |
-| `copyYear` | Boolean | `false` | Copy movie_year to sub-title tags | - | `-M` |
-| `addSeries` | Boolean | `false` | Add "series" category to non-movies | - | `-j` |
-| `includeXMLTV` | File | (none) | Include XMLTV file in output | - | `-J` |
-| `useTVGuide` | Boolean | `false` | Use tvguide.com instead of gracenote.com | - | `-z` |
-
-### Notes
-
-- Configuration file values can be overridden by command line options
-- The configuration file supports comments (lines starting with `#`)
-- Empty lines are ignored
-- Values are trimmed of leading/trailing whitespace
-- Boolean options (like `outformat=xtvd`) are case-insensitive
+| Argument | Description | Type | Default |
+| -------------- | --------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------- |
+| `--lineupId` | Lineup ID; You can find this at https://tvlistings.gracenote.com/grid-affiliates.html?aid=orbebb | String | `USA-lineupId-DEFAULT` (Attenna) |
+| `--timespan` | Either 3 or 6 hours of shows | Integer | 3 |
+| `--pref` | User Preferences, comma separated list. `m` for showing music, `p` for showing pay-per-view, `h` for showing HD | String | (empty) |
+| `--postalCode` | Postal code of where shows are available. | Integer | 30309 |
+| `--userAgent` | Custom user agent string for HTTP requests. | String | Uses random if not specified |
+| `--timezone` | Timezone | String | System default |
+| `--outputFile` | Output file name (default: xmltv.xml) | String | xmltv.xml |
diff --git a/entrypoint.sh b/entrypoint.sh
index f9edb40..63b1ae5 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -1,9 +1,11 @@
#!/bin/sh
+SLEEP_TIME=${SLEEP_TIME:-10800}
+
while true; do
- DATE=$(date)
- eval /opt/zap2xml.pl "$OPT_ARGS"
- echo "Last run time: $DATE"
- echo "Will run in $SLEEPTIME seconds"
- sleep "$SLEEPTIME"
+ DATE=$(date)
+ node build/src/index.js
+ echo "Last run time: $DATE"
+ echo "Will run in $((SLEEP_TIME / 60)) minutes"
+ sleep "$SLEEP_TIME"
done
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..1da28ec
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,15 @@
+// @ts-check
+
+import eslint from "@eslint/js";
+import tseslint from "typescript-eslint";
+import prettierConfig from "eslint-plugin-prettier/recommended";
+
+export default tseslint.config(
+ eslint.configs.recommended,
+ tseslint.configs.strict,
+ tseslint.configs.stylistic,
+ prettierConfig,
+ {
+ ignores: ["build"],
+ },
+);
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..ce3dce4
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,3373 @@
+{
+ "name": "@jef/zap2xml",
+ "version": "1.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@jef/zap2xml",
+ "version": "1.0.0",
+ "dependencies": {
+ "node-fetch": "^3.3.2"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.31.0",
+ "@types/node": "^24.0.14",
+ "eslint": "^9.31.0",
+ "eslint-plugin-prettier": "^5.5.3",
+ "prettier": "^3.6.2",
+ "tsx": "^4.20.3",
+ "typescript": "^5.8.3",
+ "typescript-eslint": "^8.37.0",
+ "vitest": "^3.2.4"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz",
+ "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz",
+ "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz",
+ "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz",
+ "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz",
+ "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz",
+ "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz",
+ "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz",
+ "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz",
+ "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz",
+ "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz",
+ "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz",
+ "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz",
+ "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz",
+ "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz",
+ "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz",
+ "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz",
+ "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz",
+ "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz",
+ "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz",
+ "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz",
+ "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz",
+ "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz",
+ "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz",
+ "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
+ "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
+ "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
+ "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
+ "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.31.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.31.0.tgz",
+ "integrity": "sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.3.tgz",
+ "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.15.1",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz",
+ "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgr/core": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.3.4.tgz",
+ "integrity": "sha512-MrfbnMgfKexic6mxC4xrSBVQHSvmvhz7qtSDG5cHg4xn8kHXkPltUY44R5u4ghYf+1rXpOvC2drbMcE1rZ3a2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/pkgr"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz",
+ "integrity": "sha512-NEySIFvMY0ZQO+utJkgoMiCAjMrGvnbDLHvcmlA33UXJpYBCvlBEbMMtV837uCkS+plG2umfhn0T5mMAxGrlRA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.45.1.tgz",
+ "integrity": "sha512-ujQ+sMXJkg4LRJaYreaVx7Z/VMgBBd89wGS4qMrdtfUFZ+TSY5Rs9asgjitLwzeIbhwdEhyj29zhst3L1lKsRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.45.1.tgz",
+ "integrity": "sha512-FSncqHvqTm3lC6Y13xncsdOYfxGSLnP+73k815EfNmpewPs+EyM49haPS105Rh4aF5mJKywk9X0ogzLXZzN9lA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.45.1.tgz",
+ "integrity": "sha512-2/vVn/husP5XI7Fsf/RlhDaQJ7x9zjvC81anIVbr4b/f0xtSmXQTFcGIQ/B1cXIYM6h2nAhJkdMHTnD7OtQ9Og==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.45.1.tgz",
+ "integrity": "sha512-4g1kaDxQItZsrkVTdYQ0bxu4ZIQ32cotoQbmsAnW1jAE4XCMbcBPDirX5fyUzdhVCKgPcrwWuucI8yrVRBw2+g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.45.1.tgz",
+ "integrity": "sha512-L/6JsfiL74i3uK1Ti2ZFSNsp5NMiM4/kbbGEcOCps99aZx3g8SJMO1/9Y0n/qKlWZfn6sScf98lEOUe2mBvW9A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.45.1.tgz",
+ "integrity": "sha512-RkdOTu2jK7brlu+ZwjMIZfdV2sSYHK2qR08FUWcIoqJC2eywHbXr0L8T/pONFwkGukQqERDheaGTeedG+rra6Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.45.1.tgz",
+ "integrity": "sha512-3kJ8pgfBt6CIIr1o+HQA7OZ9mp/zDk3ctekGl9qn/pRBgrRgfwiffaUmqioUGN9hv0OHv2gxmvdKOkARCtRb8Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.45.1.tgz",
+ "integrity": "sha512-k3dOKCfIVixWjG7OXTCOmDfJj3vbdhN0QYEqB+OuGArOChek22hn7Uy5A/gTDNAcCy5v2YcXRJ/Qcnm4/ma1xw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.45.1.tgz",
+ "integrity": "sha512-PmI1vxQetnM58ZmDFl9/Uk2lpBBby6B6rF4muJc65uZbxCs0EA7hhKCk2PKlmZKuyVSHAyIw3+/SiuMLxKxWog==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.45.1.tgz",
+ "integrity": "sha512-9UmI0VzGmNJ28ibHW2GpE2nF0PBQqsyiS4kcJ5vK+wuwGnV5RlqdczVocDSUfGX/Na7/XINRVoUgJyFIgipoRg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.45.1.tgz",
+ "integrity": "sha512-7nR2KY8oEOUTD3pBAxIBBbZr0U7U+R9HDTPNy+5nVVHDXI4ikYniH1oxQz9VoB5PbBU1CZuDGHkLJkd3zLMWsg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.45.1.tgz",
+ "integrity": "sha512-nlcl3jgUultKROfZijKjRQLUu9Ma0PeNv/VFHkZiKbXTBQXhpytS8CIj5/NfBeECZtY2FJQubm6ltIxm/ftxpw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.45.1.tgz",
+ "integrity": "sha512-HJV65KLS51rW0VY6rvZkiieiBnurSzpzore1bMKAhunQiECPuxsROvyeaot/tcK3A3aGnI+qTHqisrpSgQrpgA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.45.1.tgz",
+ "integrity": "sha512-NITBOCv3Qqc6hhwFt7jLV78VEO/il4YcBzoMGGNxznLgRQf43VQDae0aAzKiBeEPIxnDrACiMgbqjuihx08OOw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.45.1.tgz",
+ "integrity": "sha512-+E/lYl6qu1zqgPEnTrs4WysQtvc/Sh4fC2nByfFExqgYrqkKWp1tWIbe+ELhixnenSpBbLXNi6vbEEJ8M7fiHw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.45.1.tgz",
+ "integrity": "sha512-a6WIAp89p3kpNoYStITT9RbTbTnqarU7D8N8F2CV+4Cl9fwCOZraLVuVFvlpsW0SbIiYtEnhCZBPLoNdRkjQFw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.45.1.tgz",
+ "integrity": "sha512-T5Bi/NS3fQiJeYdGvRpTAP5P02kqSOpqiopwhj0uaXB6nzs5JVi2XMJb18JUSKhCOX8+UE1UKQufyD6Or48dJg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.45.1.tgz",
+ "integrity": "sha512-lxV2Pako3ujjuUe9jiU3/s7KSrDfH6IgTSQOnDWr9aJ92YsFd7EurmClK0ly/t8dzMkDtd04g60WX6yl0sGfdw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.45.1.tgz",
+ "integrity": "sha512-M/fKi4sasCdM8i0aWJjCSFm2qEnYRR8AMLG2kxp6wD13+tMGA4Z1tVAuHkNRjud5SW2EM3naLuK35w9twvf6aA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/chai": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz",
+ "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/deep-eql": "*"
+ }
+ },
+ "node_modules/@types/deep-eql": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.0.14",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz",
+ "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.8.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.37.0.tgz",
+ "integrity": "sha512-jsuVWeIkb6ggzB+wPCsR4e6loj+rM72ohW6IBn2C+5NCvfUVY8s33iFPySSVXqtm5Hu29Ne/9bnA0JmyLmgenA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.37.0",
+ "@typescript-eslint/type-utils": "8.37.0",
+ "@typescript-eslint/utils": "8.37.0",
+ "@typescript-eslint/visitor-keys": "8.37.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.37.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.37.0.tgz",
+ "integrity": "sha512-kVIaQE9vrN9RLCQMQ3iyRlVJpTiDUY6woHGb30JDkfJErqrQEmtdWH3gV0PBAfGZgQXoqzXOO0T3K6ioApbbAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.37.0",
+ "@typescript-eslint/types": "8.37.0",
+ "@typescript-eslint/typescript-estree": "8.37.0",
+ "@typescript-eslint/visitor-keys": "8.37.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.37.0.tgz",
+ "integrity": "sha512-BIUXYsbkl5A1aJDdYJCBAo8rCEbAvdquQ8AnLb6z5Lp1u3x5PNgSSx9A/zqYc++Xnr/0DVpls8iQ2cJs/izTXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.37.0",
+ "@typescript-eslint/types": "^8.37.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.37.0.tgz",
+ "integrity": "sha512-0vGq0yiU1gbjKob2q691ybTg9JX6ShiVXAAfm2jGf3q0hdP6/BruaFjL/ManAR/lj05AvYCH+5bbVo0VtzmjOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.37.0",
+ "@typescript-eslint/visitor-keys": "8.37.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.37.0.tgz",
+ "integrity": "sha512-1/YHvAVTimMM9mmlPvTec9NP4bobA1RkDbMydxG8omqwJJLEW/Iy2C4adsAESIXU3WGLXFHSZUU+C9EoFWl4Zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.37.0.tgz",
+ "integrity": "sha512-SPkXWIkVZxhgwSwVq9rqj/4VFo7MnWwVaRNznfQDc/xPYHjXnPfLWn+4L6FF1cAz6e7dsqBeMawgl7QjUMj4Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.37.0",
+ "@typescript-eslint/typescript-estree": "8.37.0",
+ "@typescript-eslint/utils": "8.37.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.37.0.tgz",
+ "integrity": "sha512-ax0nv7PUF9NOVPs+lmQ7yIE7IQmAf8LGcXbMvHX5Gm+YJUYNAl340XkGnrimxZ0elXyoQJuN5sbg6C4evKA4SQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.37.0.tgz",
+ "integrity": "sha512-zuWDMDuzMRbQOM+bHyU4/slw27bAUEcKSKKs3hcv2aNnc/tvE/h7w60dwVw8vnal2Pub6RT1T7BI8tFZ1fE+yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.37.0",
+ "@typescript-eslint/tsconfig-utils": "8.37.0",
+ "@typescript-eslint/types": "8.37.0",
+ "@typescript-eslint/visitor-keys": "8.37.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.37.0.tgz",
+ "integrity": "sha512-TSFvkIW6gGjN2p6zbXo20FzCABbyUAuq6tBvNRGsKdsSQ6a7rnV6ADfZ7f4iI3lIiXc4F4WWvtUfDw9CJ9pO5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.37.0",
+ "@typescript-eslint/types": "8.37.0",
+ "@typescript-eslint/typescript-estree": "8.37.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.37.0.tgz",
+ "integrity": "sha512-YzfhzcTnZVPiLfP/oeKtDp2evwvHLMe0LOy7oe+hb9KKIumLNohYS9Hgp1ifwpu42YWxhZE8yieggz6JpqO/1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.37.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
+ "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/chai": "^5.2.2",
+ "@vitest/spy": "3.2.4",
+ "@vitest/utils": "3.2.4",
+ "chai": "^5.2.0",
+ "tinyrainbow": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz",
+ "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "3.2.4",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.17"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
+ "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz",
+ "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "3.2.4",
+ "pathe": "^2.0.3",
+ "strip-literal": "^3.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz",
+ "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "3.2.4",
+ "magic-string": "^0.30.17",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
+ "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyspy": "^4.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
+ "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "3.2.4",
+ "loupe": "^3.1.4",
+ "tinyrainbow": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/chai": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz",
+ "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/check-error": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-eql": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.6",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz",
+ "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.6",
+ "@esbuild/android-arm": "0.25.6",
+ "@esbuild/android-arm64": "0.25.6",
+ "@esbuild/android-x64": "0.25.6",
+ "@esbuild/darwin-arm64": "0.25.6",
+ "@esbuild/darwin-x64": "0.25.6",
+ "@esbuild/freebsd-arm64": "0.25.6",
+ "@esbuild/freebsd-x64": "0.25.6",
+ "@esbuild/linux-arm": "0.25.6",
+ "@esbuild/linux-arm64": "0.25.6",
+ "@esbuild/linux-ia32": "0.25.6",
+ "@esbuild/linux-loong64": "0.25.6",
+ "@esbuild/linux-mips64el": "0.25.6",
+ "@esbuild/linux-ppc64": "0.25.6",
+ "@esbuild/linux-riscv64": "0.25.6",
+ "@esbuild/linux-s390x": "0.25.6",
+ "@esbuild/linux-x64": "0.25.6",
+ "@esbuild/netbsd-arm64": "0.25.6",
+ "@esbuild/netbsd-x64": "0.25.6",
+ "@esbuild/openbsd-arm64": "0.25.6",
+ "@esbuild/openbsd-x64": "0.25.6",
+ "@esbuild/openharmony-arm64": "0.25.6",
+ "@esbuild/sunos-x64": "0.25.6",
+ "@esbuild/win32-arm64": "0.25.6",
+ "@esbuild/win32-ia32": "0.25.6",
+ "@esbuild/win32-x64": "0.25.6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.31.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.31.0.tgz",
+ "integrity": "sha512-QldCVh/ztyKJJZLr4jXNUByx3gR+TDYZCRXEktiZoUR3PGy4qCmSbkxcIle8GEwGpb5JBZazlaJ/CxLidXdEbQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.0",
+ "@eslint/config-helpers": "^0.3.0",
+ "@eslint/core": "^0.15.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.31.0",
+ "@eslint/plugin-kit": "^0.3.1",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "10.1.8",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-config-prettier"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz",
+ "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.11.7"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz",
+ "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.4.6",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
+ "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-tsconfig": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
+ "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve-pkg-maps": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/js-tokens": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz",
+ "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loupe": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.4.tgz",
+ "integrity": "sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pathval": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+ "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.16"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/resolve-pkg-maps": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+ "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.45.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.45.1.tgz",
+ "integrity": "sha512-4iya7Jb76fVpQyLoiVpzUrsjQ12r3dM7fIVz+4NwoYvZOShknRmiv+iu9CClZml5ZLGb0XMcYLutK6w9tgxHDw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.45.1",
+ "@rollup/rollup-android-arm64": "4.45.1",
+ "@rollup/rollup-darwin-arm64": "4.45.1",
+ "@rollup/rollup-darwin-x64": "4.45.1",
+ "@rollup/rollup-freebsd-arm64": "4.45.1",
+ "@rollup/rollup-freebsd-x64": "4.45.1",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.45.1",
+ "@rollup/rollup-linux-arm-musleabihf": "4.45.1",
+ "@rollup/rollup-linux-arm64-gnu": "4.45.1",
+ "@rollup/rollup-linux-arm64-musl": "4.45.1",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.45.1",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.45.1",
+ "@rollup/rollup-linux-riscv64-gnu": "4.45.1",
+ "@rollup/rollup-linux-riscv64-musl": "4.45.1",
+ "@rollup/rollup-linux-s390x-gnu": "4.45.1",
+ "@rollup/rollup-linux-x64-gnu": "4.45.1",
+ "@rollup/rollup-linux-x64-musl": "4.45.1",
+ "@rollup/rollup-win32-arm64-msvc": "4.45.1",
+ "@rollup/rollup-win32-ia32-msvc": "4.45.1",
+ "@rollup/rollup-win32-x64-msvc": "4.45.1",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
+ "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-literal": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz",
+ "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^9.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/synckit": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.10.tgz",
+ "integrity": "sha512-n6Ze5AGOURWdQ9Kg/wqI1//4OBw9V1zuOTj7uQlpAjtpe2bhgPBpmSFXvapbP3KxcRoqo996J28kdT2ly4w9UA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pkgr/core": "^0.3.0"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/synckit"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinypool": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
+ "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
+ "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tinyspy": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz",
+ "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/tsx": {
+ "version": "4.20.3",
+ "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz",
+ "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "~0.25.0",
+ "get-tsconfig": "^4.7.5"
+ },
+ "bin": {
+ "tsx": "dist/cli.mjs"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.37.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.37.0.tgz",
+ "integrity": "sha512-TnbEjzkE9EmcO0Q2zM+GE8NQLItNAJpMmED1BdgoBMYNdqMhzlbqfdSwiRlAzEK2pA9UzVW0gzaaIzXWg2BjfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.37.0",
+ "@typescript-eslint/parser": "8.37.0",
+ "@typescript-eslint/typescript-estree": "8.37.0",
+ "@typescript-eslint/utils": "8.37.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.8.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
+ "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.5.tgz",
+ "integrity": "sha512-1mncVwJxy2C9ThLwz0+2GKZyEXuC3MyWtAAlNftlZZXZDP3AJt5FmwcMit/IGGaNZ8ZOB2BNO/HFUB+CpN0NQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.6",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.6",
+ "rollup": "^4.40.0",
+ "tinyglobby": "^0.2.14"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-node": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
+ "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.4.1",
+ "es-module-lexer": "^1.7.0",
+ "pathe": "^2.0.3",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz",
+ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/chai": "^5.2.2",
+ "@vitest/expect": "3.2.4",
+ "@vitest/mocker": "3.2.4",
+ "@vitest/pretty-format": "^3.2.4",
+ "@vitest/runner": "3.2.4",
+ "@vitest/snapshot": "3.2.4",
+ "@vitest/spy": "3.2.4",
+ "@vitest/utils": "3.2.4",
+ "chai": "^5.2.0",
+ "debug": "^4.4.1",
+ "expect-type": "^1.2.1",
+ "magic-string": "^0.30.17",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.2",
+ "std-env": "^3.9.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^0.3.2",
+ "tinyglobby": "^0.2.14",
+ "tinypool": "^1.1.1",
+ "tinyrainbow": "^2.0.0",
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0",
+ "vite-node": "3.2.4",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/debug": "^4.1.12",
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "@vitest/browser": "3.2.4",
+ "@vitest/ui": "3.2.4",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/debug": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..9d68877
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "@jef/zap2xml",
+ "version": "1.0.0",
+ "description": "JavaScript implementation of zap2xml",
+ "exports": "./src/index.ts",
+ "type": "module",
+ "engines": {
+ "node": ">=18"
+ },
+ "scripts": {
+ "test": "vitest",
+ "test:run": "vitest run",
+ "build": "tsc",
+ "dev": "tsx src/index.ts",
+ "lint": "eslint .",
+ "lint:fix": "eslint . --fix"
+ },
+ "devDependencies": {
+ "@eslint/js": "^9.31.0",
+ "@types/node": "^24.0.14",
+ "eslint": "^9.31.0",
+ "eslint-plugin-prettier": "^5.5.3",
+ "prettier": "^3.6.2",
+ "tsx": "^4.20.3",
+ "typescript": "^5.8.3",
+ "typescript-eslint": "^8.37.0",
+ "vitest": "^3.2.4"
+ },
+ "dependencies": {
+ "node-fetch": "^3.3.2"
+ },
+ "volta": {
+ "node": "22.17.1"
+ }
+}
diff --git a/src/config.ts b/src/config.ts
new file mode 100644
index 0000000..61e7758
--- /dev/null
+++ b/src/config.ts
@@ -0,0 +1,34 @@
+import { UserAgent } from "./useragents.js";
+
+export const config = {
+ baseUrl: "https://tvlistings.gracenote.com/api/grid",
+ lineupId:
+ process.env.LINEUP_ID ||
+ process.argv.find((arg) => arg.startsWith("--lineupId="))?.split("=")[1] ||
+ "USA-lineupId-DEFAULT",
+ timespan:
+ process.env.TIMESPAN ||
+ process.argv.find((arg) => arg.startsWith("--timespan="))?.split("=")[1] ||
+ "3",
+ postalCode:
+ process.env.POSTAL_CODE ||
+ process.argv
+ .find((arg) => arg.startsWith("--postalCode="))
+ ?.split("=")[1] ||
+ "30309",
+ pref:
+ process.env.PREF ||
+ process.argv.find((arg) => arg.startsWith("--pref="))?.split("=")[1] ||
+ "",
+ timezone: process.env.TZ || "America/New_York",
+ userAgent:
+ process.env.USER_AGENT ||
+ process.argv.find((arg) => arg.startsWith("--userAgent="))?.split("=")[1] ||
+ UserAgent,
+ outputFile:
+ process.env.OUTPUT_FILE ||
+ process.argv
+ .find((arg) => arg.startsWith("--outputFile="))
+ ?.split("=")[1] ||
+ "xmltv.xml",
+};
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..c06425a
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,38 @@
+import { writeFileSync } from "node:fs";
+import { getTVListings } from "./tvlistings.js";
+import { buildXmltv } from "./xmltv.js";
+import { config } from "./config.js";
+
+function isHelp() {
+ if (process.argv.includes("--help")) {
+ console.log(`
+Usage: node build/index.js [options]
+
+Options:
+--help Show this help message
+--lineupId=ID Lineup ID (default: USA-lineupId-DEFAULT)
+--timespan=NUM Timespan in hours (default: 3)
+--pref=LIST User preferences, comma separated. Can be m, p, and h (default: empty)
+--postalCode=ZIP Postal code (default: 30309)
+--userAgent=UA Custom user agent string (default: Uses random if not specified)
+--timezone=TZ Timezone (default: America/New_York)
+`);
+ process.exit(0);
+ }
+}
+
+async function main() {
+ try {
+ isHelp();
+
+ const data = await getTVListings();
+ const xml = buildXmltv(data);
+
+ console.log("Writing XMLTV file");
+ writeFileSync(config.outputFile, xml, { encoding: "utf-8" });
+ } catch (err) {
+ console.error("Error fetching GridApiResponse:", err);
+ }
+}
+
+void main();
diff --git a/src/tvlistings.test.ts b/src/tvlistings.test.ts
new file mode 100644
index 0000000..f167317
--- /dev/null
+++ b/src/tvlistings.test.ts
@@ -0,0 +1,237 @@
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
+import type { GridApiResponse } from "./tvlistings.js";
+import { getTVListings } from "./tvlistings.js";
+
+// Mock fetch globally
+const mockFetch = vi.fn();
+global.fetch = mockFetch;
+
+const mockGridApiResponse: GridApiResponse = {
+ channels: [
+ {
+ callSign: "KOMODT",
+ affiliateName: "AMERICAN BROADCASTING COMPANY",
+ affiliateCallSign: null,
+ channelId: "19629",
+ channelNo: "4.1",
+ events: [
+ {
+ callSign: "KOMODT",
+ duration: "60",
+ startTime: "2025-07-18T19:00:00Z",
+ endTime: "2025-07-18T20:00:00Z",
+ thumbnail: "p30687311_b_v13_aa",
+ channelNo: "4.1",
+ filter: ["filter-news"],
+ seriesId: "SH05918266",
+ rating: "TV-PG",
+ flag: ["New"],
+ tags: ["Stereo", "CC"],
+ program: {
+ title: "GMA3",
+ id: "EP059182660025",
+ tmsId: "EP059182660025",
+ shortDesc:
+ "BIA performs; comic Zarna Garg; lifestyle contributor Lori Bergamotto; ABC News chief medical correspondent Dr. Tara Narula.",
+ season: "5",
+ releaseYear: null,
+ episode: "217",
+ episodeTitle: "Special Episode",
+ seriesId: "SH05918266",
+ isGeneric: "0",
+ },
+ },
+ ],
+ id: "196290",
+ stationGenres: [false],
+ stationFilters: ["filter-news", "filter-talk"],
+ thumbnail:
+ "//zap2it.tmsimg.com/h3/NowShowing/19629/s28708_ll_h15_ac.png?w=55",
+ },
+ ],
+};
+
+describe("getTVListings", () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ });
+
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+
+ it("should successfully fetch TV listings", async () => {
+ // Mock successful response
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => mockGridApiResponse,
+ });
+
+ const result = await getTVListings();
+
+ expect(result).toEqual(mockGridApiResponse);
+ expect(result.channels).toHaveLength(1);
+ expect(result.channels[0].callSign).toBe("KOMODT");
+ });
+
+ it("should include a User-Agent header in the request", async () => {
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => mockGridApiResponse,
+ });
+
+ await getTVListings();
+
+ const callArgs = mockFetch.mock.calls[0];
+ const headers = callArgs[1].headers;
+ expect(headers["User-Agent"]).toBeDefined();
+ expect(typeof headers["User-Agent"]).toBe("string");
+ expect(headers["User-Agent"].length).toBeGreaterThan(0);
+ });
+
+ it("should use a random User-Agent from the predefined list", async () => {
+ const expectedUserAgents = [
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0",
+ "Mozilla/5.0 (Linux; Android 13; SM-G991U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
+ ];
+
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => mockGridApiResponse,
+ });
+
+ await getTVListings();
+
+ const callArgs = mockFetch.mock.calls[0];
+ const userAgent = callArgs[1].headers["User-Agent"];
+ expect(expectedUserAgents).toContain(userAgent);
+ });
+
+ it("should throw an error when response is not ok (4xx status)", async () => {
+ mockFetch.mockResolvedValueOnce({
+ ok: false,
+ status: 404,
+ statusText: "Not Found",
+ });
+
+ await expect(getTVListings()).rejects.toThrow(
+ "Failed to fetch: 404 Not Found",
+ );
+ });
+
+ it("should throw an error when response is not ok (5xx status)", async () => {
+ mockFetch.mockResolvedValueOnce({
+ ok: false,
+ status: 500,
+ statusText: "Internal Server Error",
+ });
+
+ await expect(getTVListings()).rejects.toThrow(
+ "Failed to fetch: 500 Internal Server Error",
+ );
+ });
+
+ it("should throw an error when response is not ok (3xx status)", async () => {
+ mockFetch.mockResolvedValueOnce({
+ ok: false,
+ status: 301,
+ statusText: "Moved Permanently",
+ });
+
+ await expect(getTVListings()).rejects.toThrow(
+ "Failed to fetch: 301 Moved Permanently",
+ );
+ });
+
+ it("should handle empty channels array", async () => {
+ const emptyResponse: GridApiResponse = {
+ channels: [],
+ };
+
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => emptyResponse,
+ });
+
+ const result = await getTVListings();
+ expect(result).toEqual(emptyResponse);
+ expect(result.channels).toHaveLength(0);
+ });
+
+ it("should handle multiple channels in response", async () => {
+ const multiChannelResponse: GridApiResponse = {
+ channels: [
+ {
+ callSign: "KOMODT",
+ affiliateName: "AMERICAN BROADCASTING COMPANY",
+ affiliateCallSign: null,
+ channelId: "19629",
+ channelNo: "4.1",
+ events: [],
+ id: "196290",
+ stationGenres: [],
+ stationFilters: [],
+ thumbnail: "",
+ },
+ {
+ callSign: "KOMODT2",
+ affiliateName: "AMERICAN BROADCASTING COMPANY",
+ affiliateCallSign: null,
+ channelId: "19630",
+ channelNo: "4.2",
+ events: [],
+ id: "196300",
+ stationGenres: [],
+ stationFilters: [],
+ thumbnail: "",
+ },
+ ],
+ };
+
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => multiChannelResponse,
+ });
+
+ const result = await getTVListings();
+ expect(result.channels).toHaveLength(2);
+ expect(result.channels[0].callSign).toBe("KOMODT");
+ expect(result.channels[1].callSign).toBe("KOMODT2");
+ });
+
+ it("should handle network errors", async () => {
+ const networkError = new Error("Network error");
+ mockFetch.mockRejectedValueOnce(networkError);
+
+ await expect(getTVListings()).rejects.toThrow("Network error");
+ });
+
+ it("should handle JSON parsing errors", async () => {
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => {
+ throw new Error("Invalid JSON");
+ },
+ });
+
+ await expect(getTVListings()).rejects.toThrow("Invalid JSON");
+ });
+
+ it("should handle malformed JSON response", async () => {
+ mockFetch.mockResolvedValueOnce({
+ ok: true,
+ json: async () => {
+ throw new SyntaxError("Unexpected token < in JSON at position 0");
+ },
+ });
+
+ await expect(getTVListings()).rejects.toThrow(
+ "Unexpected token < in JSON at position 0",
+ );
+ });
+});
diff --git a/src/tvlistings.ts b/src/tvlistings.ts
new file mode 100644
index 0000000..09779da
--- /dev/null
+++ b/src/tvlistings.ts
@@ -0,0 +1,117 @@
+import { config } from "./config.js";
+
+export interface Program {
+ /** "title": "GMA3" */
+ title: string;
+ /** "id": "EP059182660025" */
+ id: string;
+ /** "tmsId": "EP059182660025" */
+ tmsId: string;
+ /** "shortDesc": "BIA performs; comic Zarna Garg; lifestyle contributor Lori Bergamotto; ABC News chief medical correspondent Dr. Tara Narula." */
+ shortDesc: string;
+ /** "season": "5" */
+ season: string;
+ /** "releaseYear": null */
+ releaseYear: string | null;
+ /** "episode": "217" */
+ episode: string;
+ /** "episodeTitle": null */
+ episodeTitle: string | null;
+ /** "seriesId": "SH05918266" */
+ seriesId: string;
+ /** "isGeneric": "0" */
+ isGeneric: string;
+}
+
+export interface Event {
+ /** "callSign": "KOMODT" */
+ callSign: string;
+ /** "duration": "60" */
+ duration: string;
+ /** "startTime": "2025-07-18T19:00:00Z" */
+ startTime: string;
+ /** "endTime": "2025-07-18T20:00:00Z" */
+ endTime: string;
+ /** "thumbnail": "p30687311_b_v13_aa" */
+ thumbnail: string;
+ /** "channelNo": "4.1" */
+ channelNo: string;
+ /** "filter": ["filter-news"] */
+ filter: string[];
+ /** "seriesId": "SH05918266" */
+ seriesId: string;
+ /** "rating": "TV-PG" */
+ rating: string;
+ /** "flag": ["New"] */
+ flag: string[];
+ /** "tags": ["Stereo", "CC"] */
+ tags: string[];
+ /** "program": {...} */
+ program: Program;
+}
+
+export interface Channel {
+ /** "callSign": "KOMODT" */
+ callSign: string;
+ /** "affiliateName": "AMERICAN BROADCASTING COMPANY" */
+ affiliateName: string;
+ /** "affiliateCallSign": "null" */
+ affiliateCallSign: string | null;
+ /** "channelId": "19629" */
+ channelId: string;
+ /** "channelNo": "4.1" */
+ channelNo: string;
+ /** "events": [...] */
+ events: Event[];
+ /** "id": "196290" */
+ id: string;
+ /** "stationGenres": [false] */
+ stationGenres: boolean[];
+ /** "stationFilters": ["filter-news", "filter-talk"] */
+ stationFilters: string[];
+ /** "thumbnail": "//zap2it.tmsimg.com/h3/NowShowing/19629/s28708_ll_h15_ac.png?w=55" */
+ thumbnail: string;
+}
+
+export interface GridApiResponse {
+ /** "channels": [...] */
+ channels: Channel[];
+}
+
+function buildUrl() {
+ const params = {
+ lineupId: config.lineupId,
+ timespan: config.timespan,
+ headendId: "lineupId",
+ country: "USA",
+ timezone: config.timezone,
+ postalCode: config.postalCode,
+ isOverride: "true",
+ pref: config.pref + "16,128" || "16,128",
+ aid: "orbebb",
+ languagecode: "en-us",
+ time: Math.floor(Date.now() / 1000).toString(),
+ };
+
+ const urlParams = new URLSearchParams(params).toString();
+
+ return `${config.baseUrl}?${urlParams}`;
+}
+
+export async function getTVListings(): Promise {
+ console.log("Fetching TV listings");
+
+ const response = await fetch(buildUrl(), {
+ headers: {
+ "User-Agent": config.userAgent,
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(
+ `Failed to fetch: ${response.status} ${response.statusText}`,
+ );
+ }
+
+ return (await response.json()) as GridApiResponse;
+}
diff --git a/src/useragents.ts b/src/useragents.ts
new file mode 100644
index 0000000..fccae0e
--- /dev/null
+++ b/src/useragents.ts
@@ -0,0 +1,12 @@
+const userAgents = [
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15",
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
+ "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:125.0) Gecko/20100101 Firefox/125.0",
+ "Mozilla/5.0 (Linux; Android 13; SM-G991U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36",
+ "Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1",
+];
+
+export const UserAgent =
+ userAgents[Math.floor(Math.random() * userAgents.length)];
diff --git a/src/xmltv.test.ts b/src/xmltv.test.ts
new file mode 100644
index 0000000..8687461
--- /dev/null
+++ b/src/xmltv.test.ts
@@ -0,0 +1,333 @@
+import { describe, expect, it } from "vitest";
+import type { GridApiResponse } from "./tvlistings.js";
+import {
+ buildChannelsXml,
+ buildProgrammesXml,
+ buildXmltv,
+ escapeXml,
+ formatDate,
+} from "./xmltv.js";
+
+const mockData: GridApiResponse = {
+ channels: [
+ {
+ callSign: "KOMODT",
+ affiliateName: "AMERICAN BROADCASTING COMPANY",
+ affiliateCallSign: null,
+ channelId: "19629",
+ channelNo: "4.1",
+ events: [
+ {
+ callSign: "KOMODT",
+ duration: "60",
+ startTime: "2025-07-18T19:00:00Z",
+ endTime: "2025-07-18T20:00:00Z",
+ thumbnail: "p30687311_b_v13_aa",
+ channelNo: "4.1",
+ filter: ["filter-news"],
+ seriesId: "SH05918266",
+ rating: "TV-PG",
+ flag: ["New"],
+ tags: ["Stereo", "CC"],
+ program: {
+ title: "GMA3",
+ id: "EP059182660025",
+ tmsId: "EP059182660025",
+ shortDesc:
+ "BIA performs; comic Zarna Garg; lifestyle contributor Lori Bergamotto; ABC News chief medical correspondent Dr. Tara Narula.",
+ season: "5",
+ releaseYear: null,
+ episode: "217",
+ episodeTitle: "Special Episode",
+ seriesId: "SH05918266",
+ isGeneric: "0",
+ },
+ },
+ ],
+ id: "196290",
+ stationGenres: [false],
+ stationFilters: ["filter-news", "filter-talk"],
+ thumbnail:
+ "//zap2it.tmsimg.com/h3/NowShowing/19629/s28708_ll_h15_ac.png?w=55",
+ },
+ ],
+};
+
+describe("buildXmltv", () => {
+ it("should generate valid XML structure", () => {
+ const result = buildXmltv(mockData);
+ expect(result).toContain('');
+ expect(result).toContain('');
+ expect(result).toContain("");
+ });
+
+ it("should include channel information", () => {
+ const result = buildXmltv(mockData);
+ expect(result).toContain('');
+ expect(result).toContain("KOMODT");
+ expect(result).toContain(
+ "AMERICAN BROADCASTING COMPANY",
+ );
+ expect(result).toContain("4.1");
+ });
+
+ it("should include programme information", () => {
+ const result = buildXmltv(mockData);
+ expect(result).toContain(
+ '',
+ );
+ expect(result).toContain("GMA3");
+ expect(result).toContain("Special Episode");
+ expect(result).toContain(
+ "BIA performs; comic Zarna Garg; lifestyle contributor Lori Bergamotto; ABC News chief medical correspondent Dr. Tara Narula.",
+ );
+ });
+
+ it("should include rating information", () => {
+ const result = buildXmltv(mockData);
+ expect(result).toContain("TV-PG");
+ });
+
+ it("should include categories from flags and tags", () => {
+ const result = buildXmltv(mockData);
+ expect(result).toContain("New");
+ expect(result).toContain("Stereo");
+ expect(result).toContain("CC");
+ });
+
+ it("should include episode information", () => {
+ const result = buildXmltv(mockData);
+ expect(result).toContain('5');
+ expect(result).toContain('217');
+ expect(result).toContain(
+ 'SH05918266',
+ );
+ });
+
+ it("should handle empty data gracefully", () => {
+ const emptyData: GridApiResponse = { channels: [] };
+ const result = buildXmltv(emptyData);
+ expect(result).toContain('');
+ expect(result).toContain('');
+ expect(result).toContain("");
+ expect(result).not.toContain(" {
+ const minimalData: GridApiResponse = {
+ channels: [
+ {
+ callSign: "TEST",
+ affiliateName: "",
+ affiliateCallSign: null,
+ channelId: "123",
+ channelNo: "",
+ events: [
+ {
+ callSign: "TEST",
+ duration: "30",
+ startTime: "2025-07-18T19:00:00Z",
+ endTime: "2025-07-18T19:30:00Z",
+ thumbnail: "",
+ channelNo: "",
+ filter: [],
+ seriesId: "",
+ rating: "",
+ flag: [],
+ tags: [],
+ program: {
+ title: "Test Show",
+ id: "TEST123",
+ tmsId: "TEST123",
+ shortDesc: "",
+ season: "",
+ releaseYear: null,
+ episode: "",
+ episodeTitle: null,
+ seriesId: "",
+ isGeneric: "0",
+ },
+ },
+ ],
+ id: "123",
+ stationGenres: [],
+ stationFilters: [],
+ thumbnail: "",
+ },
+ ],
+ };
+ const result = buildXmltv(minimalData);
+ expect(result).toContain('');
+ expect(result).toContain("TEST");
+ expect(result).toContain(
+ '',
+ );
+ expect(result).toContain("Test Show");
+ expect(result).not.toContain("");
+ expect(result).not.toContain("");
+ expect(result).not.toContain("");
+ expect(result).not.toContain("");
+ expect(result).not.toContain(" {
+ it("should escape XML special characters", () => {
+ expect(escapeXml("&")).toBe("&");
+ expect(escapeXml("<")).toBe("<");
+ expect(escapeXml(">")).toBe(">");
+ expect(escapeXml('"')).toBe(""");
+ expect(escapeXml("'")).toBe("'");
+ });
+
+ it("should handle text with multiple special characters", () => {
+ expect(escapeXml("A & B < C > D \"E\" 'F'")).toBe(
+ "A & B < C > D "E" 'F'",
+ );
+ });
+
+ it("should handle normal text without special characters", () => {
+ expect(escapeXml("Normal text")).toBe("Normal text");
+ });
+
+ it("should handle empty string", () => {
+ expect(escapeXml("")).toBe("");
+ });
+});
+
+describe("formatDate", () => {
+ it("should format ISO date string correctly", () => {
+ expect(formatDate("2025-07-18T19:00:00Z")).toBe("20250718190000 +0000");
+ expect(formatDate("2025-12-31T23:59:59Z")).toBe("20251231235959 +0000");
+ });
+
+ it("should handle different times", () => {
+ expect(formatDate("2025-01-01T00:00:00Z")).toBe("20250101000000 +0000");
+ expect(formatDate("2025-06-15T12:30:45Z")).toBe("20250615123045 +0000");
+ });
+
+ it("should handle edge cases", () => {
+ expect(formatDate("2025-02-28T23:59:59Z")).toBe("20250228235959 +0000");
+ expect(formatDate("2025-03-01T00:00:00Z")).toBe("20250301000000 +0000");
+ });
+});
+
+describe("buildChannelsXml", () => {
+ it("should build channel XML correctly", () => {
+ const result = buildChannelsXml(mockData);
+ expect(result).toContain('');
+ expect(result).toContain("KOMODT");
+ expect(result).toContain(
+ "AMERICAN BROADCASTING COMPANY",
+ );
+ expect(result).toContain("4.1");
+ expect(result).toContain(
+ '',
+ );
+ });
+
+ it("should handle channels without optional fields", () => {
+ const minimalChannel: GridApiResponse = {
+ channels: [
+ {
+ callSign: "TEST",
+ affiliateName: "",
+ affiliateCallSign: null,
+ channelId: "123",
+ channelNo: "",
+ events: [],
+ id: "123",
+ stationGenres: [],
+ stationFilters: [],
+ thumbnail: "",
+ },
+ ],
+ };
+ const result = buildChannelsXml(minimalChannel);
+ expect(result).toContain('');
+ expect(result).toContain("TEST");
+ expect(result).not.toContain(" {
+ it("should build programme XML correctly", () => {
+ const result = buildProgrammesXml(mockData);
+ expect(result).toContain(
+ '',
+ );
+ expect(result).toContain("GMA3");
+ expect(result).toContain("Special Episode");
+ expect(result).toContain(
+ "BIA performs; comic Zarna Garg; lifestyle contributor Lori Bergamotto; ABC News chief medical correspondent Dr. Tara Narula.",
+ );
+ expect(result).toContain("TV-PG");
+ expect(result).toContain("New");
+ expect(result).toContain("Stereo");
+ expect(result).toContain("CC");
+ expect(result).toContain('5');
+ expect(result).toContain('217');
+ expect(result).toContain(
+ 'SH05918266',
+ );
+ expect(result).toContain('');
+ });
+
+ it("should handle programmes without optional fields", () => {
+ const minimalProgramme: GridApiResponse = {
+ channels: [
+ {
+ callSign: "TEST",
+ affiliateName: "",
+ affiliateCallSign: null,
+ channelId: "123",
+ channelNo: "",
+ events: [
+ {
+ callSign: "TEST",
+ duration: "30",
+ startTime: "2025-07-18T19:00:00Z",
+ endTime: "2025-07-18T19:30:00Z",
+ thumbnail: "",
+ channelNo: "",
+ filter: [],
+ seriesId: "",
+ rating: "",
+ flag: [],
+ tags: [],
+ program: {
+ title: "Test Show",
+ id: "TEST123",
+ tmsId: "TEST123",
+ shortDesc: "",
+ season: "",
+ releaseYear: null,
+ episode: "",
+ episodeTitle: null,
+ seriesId: "",
+ isGeneric: "0",
+ },
+ },
+ ],
+ id: "123",
+ stationGenres: [],
+ stationFilters: [],
+ thumbnail: "",
+ },
+ ],
+ };
+ const result = buildProgrammesXml(minimalProgramme);
+ expect(result).toContain(
+ '',
+ );
+ expect(result).toContain("Test Show");
+ expect(result).not.toContain("");
+ expect(result).not.toContain("");
+ expect(result).not.toContain("");
+ expect(result).not.toContain("");
+ expect(result).not.toContain("/g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
+export function formatDate(dateStr: string): string {
+ // Input: "2025-07-18T19:00:00Z"
+ // Output: "20250718190000 +0000"
+ const d = new Date(dateStr);
+ const pad = (n: number) => n.toString().padStart(2, "0");
+ const YYYY = d.getUTCFullYear();
+ const MM = pad(d.getUTCMonth() + 1);
+ const DD = pad(d.getUTCDate());
+ const hh = pad(d.getUTCHours());
+ const mm = pad(d.getUTCMinutes());
+ const ss = pad(d.getUTCSeconds());
+ return `${YYYY}${MM}${DD}${hh}${mm}${ss} +0000`;
+}
+
+export function buildChannelsXml(data: GridApiResponse): string {
+ let xml = "";
+
+ for (const channel of data.channels) {
+ xml += ` \n`;
+ xml += ` ${escapeXml(channel.callSign)}\n`;
+
+ if (channel.affiliateName) {
+ xml += ` ${escapeXml(
+ channel.affiliateName,
+ )}\n`;
+ }
+
+ if (channel.channelNo) {
+ xml += ` ${escapeXml(
+ channel.channelNo,
+ )}\n`;
+ }
+
+ if (channel.thumbnail) {
+ xml += ` \n`;
+ }
+
+ xml += " \n";
+ }
+ return xml;
+}
+
+export function buildProgrammesXml(data: GridApiResponse): string {
+ let xml = "";
+
+ for (const channel of data.channels) {
+ for (const event of channel.events) {
+ xml += ` \n`;
+
+ xml += ` ${escapeXml(event.program.title)}\n`;
+
+ if (event.program.episodeTitle) {
+ xml += ` ${escapeXml(
+ event.program.episodeTitle,
+ )}\n`;
+ }
+
+ if (event.program.shortDesc) {
+ xml += ` ${escapeXml(event.program.shortDesc)}\n`;
+ }
+
+ if (event.rating) {
+ xml += ` ${escapeXml(
+ event.rating,
+ )}\n`;
+ }
+
+ if (event.flag && event.flag.length > 0) {
+ for (const flag of event.flag) {
+ xml += ` ${escapeXml(flag)}\n`;
+ }
+ }
+
+ if (event.tags && event.tags.length > 0) {
+ for (const tag of event.tags) {
+ xml += ` ${escapeXml(tag)}\n`;
+ }
+ }
+
+ if (event.program.season) {
+ xml += ` ${escapeXml(
+ event.program.season,
+ )}\n`;
+ }
+
+ if (event.program.episode) {
+ xml += ` ${escapeXml(
+ event.program.episode,
+ )}\n`;
+ }
+
+ if (event.program.seriesId) {
+ xml += ` ${escapeXml(
+ event.program.seriesId,
+ )}\n`;
+ }
+
+ if (event.thumbnail) {
+ xml += ` \n`;
+ }
+
+ xml += " \n";
+ }
+ }
+
+ return xml;
+}
+
+export function buildXmltv(data: GridApiResponse): string {
+ console.log("Building XMLTV file");
+
+ let xml =
+ '\n\n';
+ xml += buildChannelsXml(data);
+ xml += buildProgrammesXml(data);
+ xml += "\n";
+
+ return xml;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..3421744
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "compilerOptions": {
+ "rootDir": ".",
+ "outDir": "build",
+ "module": "Node16",
+ "moduleResolution": "Node16",
+ "target": "ESNext",
+ "skipLibCheck": true
+ },
+ "include": [
+ "src/**/*.ts"
+ ],
+ "exclude": [
+ "src/**/*.test.ts"
+ ]
+}
diff --git a/zap2xml.pl b/zap2xml.pl
deleted file mode 100755
index 937c83d..0000000
--- a/zap2xml.pl
+++ /dev/null
@@ -1,1596 +0,0 @@
-#!/usr/bin/env perl
-
-BEGIN { $SIG{__DIE__} = sub {
- return if $^S;
- my $msg = join(" ", @_);
- print STDERR "$msg";
- if ($msg =~ /can't locate/i) {
- print "\nSee homepage for tips on installing missing modules (example: \"perl -MCPAN -e shell\")\n";
- if ($^O eq 'MSWin32') {
- print "Use \"ppm install\" on windows\n";
- }
- }
- if ($^O eq 'MSWin32') {
- if ($msg =~ /uri.pm/i && $msg =~ /temp/i) {
- print "\nIf your scanner deleted the perl URI.pm file see the homepage for tips\n";
- if ($msg =~ /(\ .\:.+?par-.+?\\)/) {
- print "(Delete the $1 folder and retry)\n";
- }
- }
- sleep(5);
- }
- exit 1;
-}}
-
-use Compress::Zlib;
-use Encode;
-use File::Basename;
-use File::Copy;
-use Getopt::Std;
-use HTTP::Cookies;
-use URI;
-use URI::Escape;
-use LWP::UserAgent;
-use LWP::ConnCache;
-use POSIX;
-use Time::Local;
-use Time::Piece;
-use JSON;
-
-no warnings 'utf8';
-
-STDOUT->autoflush(1);
-STDERR->autoflush(1);
-
-$VERSION = "2018-12-01";
-print "zap2xml ($VERSION)\nCommand line: $0 " . join(" ",@ARGV) . "\n";
-
-%options=();
-getopts("?aA:bB:c:C:d:DeE:Fgi:IjJ:l:Lm:Mn:N:o:Op:P:qRr:s:S:t:Tu:UwWxY:zZ:89",\%options);
-
-$homeDir = $ENV{HOME};
-$homeDir = $ENV{USERPROFILE} if !defined($homeDir);
-$homeDir = '.' if !defined($homeDir);
-$confFile = $homeDir . '/.zap2xmlrc';
-
-# Defaults
-$start = 0;
-$days = 7;
-$ncdays = 0;
-$ncsdays = 0;
-$ncmday = -1;
-$retries = 3;
-$outFile = 'xmltv.xml';
-$outFile = 'xtvd.xml' if defined $options{x};
-$cacheDir = 'cache';
-$lang = 'en';
-$userEmail = '';
-$password = '';
-$proxy;
-$postalcode;
-$country;
-$lineupId;
-$device;
-$sleeptime = 0;
-$allChan = 0;
-$shiftMinutes = 0;
-
-$userAgent = $ENV{USER_AGENT} || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36';
-
-$outputXTVD = 0;
-$lineuptype;
-$lineupname;
-$lineuplocation;
-
-$zapToken;
-$zapPref='-';
-%zapFavorites=();
-%sidCache=();
-
-$sTBA = "\\bTBA\\b|To Be Announced";
-
-%tvgfavs=();
-
-&HELP_MESSAGE() if defined $options{'?'};
-
-$confFile = $options{C} if defined $options{C};
-# read config file
-if (open (CONF, $confFile))
-{
- &pout("Reading config file: $confFile\n");
- while ()
- {
- s/#.*//; # comments
- if (/^\s*$/i) { }
- elsif (/^\s*start\s*=\s*(\d+)/i) { $start = $1; }
- elsif (/^\s*days\s*=\s*(\d+)/i) { $days = $1; }
- elsif (/^\s*ncdays\s*=\s*(\d+)/i) { $ncdays = $1; }
- elsif (/^\s*ncsdays\s*=\s*(\d+)/i) { $ncsdays = $1; }
- elsif (/^\s*ncmday\s*=\s*(\d+)/i) { $ncmday = $1; }
- elsif (/^\s*retries\s*=\s*(\d+)/i) { $retries = $1; }
- elsif (/^\s*user[\w\s]*=\s*(.+)/i) { $userEmail = &rtrim($1); }
- elsif (/^\s*pass[\w\s]*=\s*(.+)/i) { $password = &rtrim($1); }
- elsif (/^\s*cache\s*=\s*(.+)/i) { $cacheDir = &rtrim($1); }
- elsif (/^\s*icon\s*=\s*(.+)/i) { $iconDir = &rtrim($1); }
- elsif (/^\s*trailer\s*=\s*(.+)/i) { $trailerDir = &rtrim($1); }
- elsif (/^\s*lang\s*=\s*(.+)/i) { $lang = &rtrim($1); }
- elsif (/^\s*outfile\s*=\s*(.+)/i) { $outFile = &rtrim($1); }
- elsif (/^\s*proxy\s*=\s*(.+)/i) { $proxy = &rtrim($1); }
- elsif (/^\s*outformat\s*=\s*(.+)/i) { $outputXTVD = 1 if $1 =~ /xtvd/i; }
- elsif (/^\s*lineupid\s*=\s*(.+)/i) { $lineupId = &rtrim($1); }
- elsif (/^\s*lineupname\s*=\s*(.+)/i) { $lineupname = &rtrim($1); }
- elsif (/^\s*lineuptype\s*=\s*(.+)/i) { $lineuptype = &rtrim($1); }
- elsif (/^\s*lineuplocation\s*=\s*(.+)/i) { $lineuplocation = &rtrim($1); }
- elsif (/^\s*postalcode\s*=\s*(.+)/i) { $postalcode = &rtrim($1); }
- else
- {
- die "Oddline in config file \"$confFile\".\n\t$_";
- }
- }
- close (CONF);
-}
-&HELP_MESSAGE() if !(%options) && $userEmail eq '';
-
-$cacheDir = $options{c} if defined $options{c};
-$days = $options{d} if defined $options{d};
-$ncdays = $options{n} if defined $options{n};
-$ncsdays = $options{N} if defined $options{N};
-$ncmday = $options{B} if defined $options{B};
-$start = $options{s} if defined $options{s};
-$retries = $options{r} if defined $options{r};
-$iconDir = $options{i} if defined $options{i};
-$trailerDir = $options{t} if defined $options{t};
-$lang = $options{l} if defined $options{l};
-$outFile = $options{o} if defined $options{o};
-$password = $options{p} if defined $options{p};
-$userEmail = $options{u} if defined $options{u};
-$proxy = $options{P} if defined $options{P};
-$zlineupId = $options{Y} if defined $options{Y};
-$zipcode = $options{Z} if defined $options{Z};
-$includeXMLTV = $options{J} if defined $options{J} && -e $options{J};
-$outputXTVD = 1 if defined $options{x};
-$allChan = 1 if defined($options{a});
-$allChan = 1 if defined($zipcode) && defined($zlineupId);
-$sleeptime = $options{S} if defined $options{S};
-$shiftMinutes = $options{m} if defined $options{m};
-$ncdays = $days - $ncdays; # make relative to the end
-$urlRoot = 'https://tvlistings.gracenote.com/';
-$urlAssets = 'https://zap2it.tmsimg.com/assets/';
-$tvgurlRoot = 'http://mobilelistings.tvguide.com/';
-$tvgMapiRoot = 'http://mapi.tvguide.com/';
-$tvgurl = 'https://www.tvguide.com/';
-$tvgspritesurl = 'http://static.tvgcdn.net/sprites/';
-$retries = 20 if $retries > 20; # Too many
-
-my %programs = ();
-my $cp;
-my %stations = ();
-my $cs;
-my $rcs;
-my %schedule = ();
-my $sch;
-my %logos = ();
-
-my $coNum = 0;
-my $tb = 0;
-my $treq = 0;
-my $tsocks = ();
-my $expired = 0;
-my $ua;
-my $tba = 0;
-my $exp = 0;
-my @fh = ();
-
-my $XTVD_startTime;
-my $XTVD_endTime;
-
-if (! -d $cacheDir) {
- mkdir($cacheDir) or die "Can't mkdir: $!\n";
-} else {
- opendir (DIR, "$cacheDir/");
- @cacheFiles = grep(/\.html|\.js/,readdir(DIR));
- closedir (DIR);
- foreach $cacheFile (@cacheFiles) {
- $fn = "$cacheDir/$cacheFile";
- $atime = (stat($fn))[8];
- if ($atime + ( ($days + 2) * 86400) < time) {
- &pout("Deleting old cached file: $fn\n");
- &unf($fn);
- }
- }
-}
-
-my $s1 = time();
-if (defined($options{z})) {
-
- &login() if !defined($options{a}); # get favorites
- &parseTVGIcons() if defined($iconDir);
- $gridHours = 3;
- $maxCount = $days * (24 / $gridHours);
- $offset = $start * 3600 * 24 * 1000;
- $ms = &hourToMillis() + $offset;
-
- for ($count=0; $count < $maxCount; $count++) {
- $curday = int($count / (24/$gridHours)) + 1;
- if ($count == 0) {
- $XTVD_startTime = $ms;
- } elsif ($count == $maxCount - 1) {
- $XTVD_endTime = $ms + ($gridHours * 3600000) - 1;
- }
-
- $fn = "$cacheDir/$ms\.js\.gz";
- if (! -e $fn || $curday > $ncdays || $curday <= $ncsdays || $curday == $ncmday) {
- &login() if !defined($zlineupId);
- my $duration = $gridHours * 60;
- my $tvgstart = substr($ms, 0, -3);
- $rs = &getURL($tvgurlRoot . "Listingsweb/ws/rest/schedules/$zlineupId/start/$tvgstart/duration/$duration", 1);
- last if ($rs eq '');
- $rc = Encode::encode('utf8', $rs);
- &wbf($fn, Compress::Zlib::memGzip($rc));
- }
- &pout("[" . ($count+1) . "/" . "$maxCount] Parsing: $fn\n");
- &parseTVGGrid($fn);
-
- if (defined($options{T}) && $tba) {
- &pout("Deleting: $fn (contains \"$sTBA\")\n");
- &unf($fn);
- }
- if ($exp) {
- &pout("Deleting: $fn (expired)\n");
- &unf($fn);
- }
- $exp = 0;
- $tba = 0;
- $ms += ($gridHours * 3600 * 1000);
- }
-
-} else {
-
- &login() if !defined($options{a}); # get favorites
- $gridHours = 3;
- $maxCount = $days * (24 / $gridHours);
- $offset = $start * 3600 * 24 * 1000;
- $ms = &hourToMillis() + $offset;
- for ($count=0; $count < $maxCount; $count++) {
- $curday = int($count / (24/$gridHours)) + 1;
- if ($count == 0) {
- $XTVD_startTime = $ms;
- } elsif ($count == $maxCount - 1) {
- $XTVD_endTime = $ms + ($gridHours * 3600000) - 1;
- }
-
- $fn = "$cacheDir/$ms\.js\.gz";
- if (! -e $fn || $curday > $ncdays || $curday <= $ncsdays || $curday == $ncmday) {
- my $zstart = substr($ms, 0, -3);
- $params = "?time=$zstart×pan=$gridHours&pref=$zapPref&";
- $params .= &getZapGParams();
- $params .= '&TMSID=&AffiliateID=lat&FromPage=TV%20Grid';
- $params .= '&ActivityID=1&OVDID=&isOverride=true';
- $rs = &getURL($urlRoot . "api/grid$params",1);
- last if ($rs eq '');
- $rc = Encode::encode('utf8', $rs);
- &wbf($fn, Compress::Zlib::memGzip($rc));
- }
-
- &pout("[" . ($count+1) . "/" . "$maxCount] Parsing: $fn\n");
- &parseJSON($fn);
-
- if (defined($options{T}) && $tba) {
- &pout("Deleting: $fn (contains \"$sTBA\")\n");
- &unf($fn);
- }
- if ($exp) {
- &pout("Deleting: $fn (expired)\n");
- &unf($fn);
- }
- $exp = 0;
- $tba = 0;
- $ms += ($gridHours * 3600 * 1000);
- }
-
-}
-my $s2 = time();
-my $tsockt = scalar(keys %tsocks);
-&pout("Downloaded " . &pl($tb, "byte")
- . " in " . &pl($treq, "http request")
- . " using " . &pl($tsockt > 0 ? $tsockt : $treq, "socket") . ".\n") if $tb > 0;
-&pout("Expired programs: $expired\n") if $expired > 0;
-&pout("Writing XML file: $outFile\n");
-open($FH, ">$outFile");
-my $enc = 'ISO-8859-1';
-if (defined($options{U})) {
- $enc = 'UTF-8';
-}
-if ($outputXTVD) {
- &printHeaderXTVD($FH, $enc);
- &printStationsXTVD($FH);
- &printLineupsXTVD($FH);
- &printSchedulesXTVD($FH);
- &printProgramsXTVD($FH);
- &printGenresXTVD($FH);
- &printFooterXTVD($FH);
-} else {
- &printHeader($FH, $enc);
- &printChannels($FH);
- if (defined($includeXMLTV)) {
- &pout("Reading XML file: $includeXMLTV\n");
- &incXML(";
-} else {
- sleep(3) if ($^O eq 'MSWin32');
-}
-
-exit 0;
-
-sub incXML {
- my ($st, $en, $FH) = @_;
- open($XF, "<$includeXMLTV");
- while (<$XF>) {
- if (/^\s*$st/../^\s*$en/) {
- print $FH $_ unless /^\s*$en/
- }
- }
- close($XF);
-}
-
-sub pl {
- my($i, $s) = @_;
- my $r = "$i $s";
- return $i == 1 ? $r : $r . "s";
-}
-
-sub pout {
- print @_ if !defined $options{q};
-}
-
-sub perr {
- warn @_;
-}
-
-sub rtrim {
- my $s = shift;
- $s =~ s/\s+$//;
- return $s;
-}
-
-sub trim {
- my $s = shift;
- $s =~ s/^\s+//;
- $s =~ s/\s+$//;
- return $s;
-}
-
-sub trim2 {
- my $s = &trim(shift);
- $s =~ s/[^\w\s\(\)\,]//gsi;
- $s =~ s/\s+/ /gsi;
- return $s;
-}
-
-sub _rtrim3 {
- my $s = shift;
- return substr($s, 0, length($s)-3);
-}
-
-sub convTime {
- my $t = shift;
- $t += $shiftMinutes * 60 * 1000;
- return strftime "%Y%m%d%H%M%S", localtime(&_rtrim3($t));
-}
-
-sub convTimeXTVD {
- my $t = shift;
- $t += $shiftMinutes * 60 * 1000;
- return strftime "%Y-%m-%dT%H:%M:%SZ", gmtime(&_rtrim3($t));
-}
-
-sub convOAD {
- return strftime "%Y%m%d", gmtime(&_rtrim3(shift));
-}
-
-sub convOADXTVD {
- return strftime "%Y-%m-%d", gmtime(&_rtrim3(shift));
-}
-
-sub convDurationXTVD {
- my $duration = shift;
- my $hour = int($duration / 3600000);
- my $minutes = int(($duration - ($hour * 3600000)) / 60000);
- return sprintf("PT%02dH%02dM", $hour, $minutes);
-}
-
-sub appendAsterisk {
- my ($title, $station, $s) = @_;
- if (defined($options{A})) {
- if (($options{A} =~ "new" && defined($schedule{$station}{$s}{new}))
- || ($options{A} =~ "live" && defined($schedule{$station}{$s}{live}))) {
- $title .= " *";
- }
- }
- return $title;
-}
-
-sub stationToChannel {
- my $s = shift;
- if (defined($options{z})) {
- return sprintf("I%s.%s.tvguide.com", $stations{$s}{number},$stations{$s}{stnNum});
- } elsif (defined($options{O})) {
- return sprintf("C%s%s.gracenote.com",$stations{$s}{number},lc($stations{$s}{name}));
- } elsif (defined($options{9})) {
- return sprintf("I%s.labs.gracenote.com",$stations{$s}{stnNum});
- }
- return sprintf("I%s.%s.gracenote.com", $stations{$s}{number},$stations{$s}{stnNum});
-}
-
-sub sortChan {
- if (defined($stations{$a}{order}) && defined($stations{$b}{order})) {
- my $c = $stations{$a}{order} <=> $stations{$b}{order};
- if ($c == 0) { return $stations{$a}{stnNum} <=> $stations{$b}{stnNum} }
- else { return $c };
- } else {
- return $stations{$a}{name} cmp $stations{$b}{name};
- }
-}
-
-sub enc {
- my $t = shift;
- if (!defined($options{U})) {$t = Encode::decode('utf8', $t);}
- if (!defined($options{E}) || $options{E} =~ /amp/) {$t =~ s/&/&/gs;}
- if (!defined($options{E}) || $options{E} =~ /quot/) {$t =~ s/"/"/gs;}
- if (!defined($options{E}) || $options{E} =~ /apos/) {$t =~ s/'/'/gs;}
- if (!defined($options{E}) || $options{E} =~ /lt/) {$t =~ s/</gs;}
- if (!defined($options{E}) || $options{E} =~ /gt/) {$t =~ s/>/>/gs;}
- if (defined($options{e})) {
- $t =~ s/([^\x20-\x7F])/'' . ord($1) . ';'/gse;
- }
- return $t;
-}
-
-sub printHeader {
- my ($FH, $enc) = @_;
- print $FH "\n";
- print $FH "\n\n";
- if (defined($options{z})) {
- print $FH "\n";
-}
-
-sub printFooter {
- my $FH = shift;
- print $FH "\n";
-}
-
-sub printChannels {
- my $FH = shift;
- for my $key ( sort sortChan keys %stations ) {
- $sname = &enc($stations{$key}{name});
- $fname = &enc($stations{$key}{fullname});
- $snum = $stations{$key}{number};
- print $FH "\t\n";
- print $FH "\t\t" . $sname . "\n" if defined($options{F}) && defined($sname);
- if (defined($snum)) {
- ©Logo($key);
- print $FH "\t\t" . $snum . " " . $sname . "\n" if ($snum ne '');
- print $FH "\t\t" . $snum . "\n" if ($snum ne '');
- }
- print $FH "\t\t" . $sname . "\n" if !defined($options{F}) && defined($sname);
- print $FH "\t\t" . $fname . "\n" if (defined($fname));
- if (defined($stations{$key}{logoURL})) {
- print $FH "\t\t\n";
- }
- print $FH "\t\n";
- }
-}
-
-sub printProgrammes {
- my $FH = shift;
- for my $station ( sort sortChan keys %stations ) {
- my $i = 0;
- my @keyArray = sort { $schedule{$station}{$a}{time} cmp $schedule{$station}{$b}{time} } keys %{$schedule{$station}};
- foreach $s (@keyArray) {
- if ($#keyArray <= $i && !defined($schedule{$station}{$s}{endtime})) {
- delete $schedule{$station}{$s};
- next;
- }
- my $p = $schedule{$station}{$s}{program};
- my $startTime = &convTime($schedule{$station}{$s}{time});
- my $startTZ = &timezone($schedule{$station}{$s}{time});
- my $endTime;
- if (defined($schedule{$station}{$s}{endtime})) {
- $endTime = $schedule{$station}{$s}{endtime};
- } else {
- $endTime = $schedule{$station}{$keyArray[$i+1]}{time};
- }
-
- my $stopTime = &convTime($endTime);
- my $stopTZ = &timezone($endTime);
-
- print $FH "\t\n";
- if (defined($programs{$p}{title})) {
- my $title = &enc($programs{$p}{title});
- $title = &appendAsterisk($title, $station, $s);
- print $FH "\t\t" . $title . "\n";
- }
-
- if (defined($programs{$p}{episode}) || (defined($options{M}) && defined($programs{$p}{movie_year}))) {
- print $FH "\t\t";
- if (defined($programs{$p}{episode})) {
- print $FH &enc($programs{$p}{episode});
- } else {
- print $FH "Movie (" . $programs{$p}{movie_year} . ")";
- }
- print $FH "\n"
- }
-
- print $FH "\t\t" . &enc($programs{$p}{description}) . "\n" if defined($programs{$p}{description});
-
- if (defined($programs{$p}{actor})
- || defined($programs{$p}{director})
- || defined($programs{$p}{writer})
- || defined($programs{$p}{producer})
- || defined($programs{$p}{preseter})
- ) {
- print $FH "\t\t\n";
- &printCredits($FH, $p, "director");
- foreach my $g (sort { $programs{$p}{actor}{$a} <=> $programs{$p}{actor}{$b} } keys %{$programs{$p}{actor}} ) {
- print $FH "\t\t\t" . &enc($g) . "\n";
- }
- &printCredits($FH, $p, "writer");
- &printCredits($FH, $p, "producer");
- &printCredits($FH, $p, "presenter");
- print $FH "\t\t\n";
- }
-
- my $date;
- if (defined($programs{$p}{movie_year})) {
- $date = $programs{$p}{movie_year};
- } elsif (defined($programs{$p}{originalAirDate}) && $p =~ /^EP|^\d/) {
- $date = &convOAD($programs{$p}{originalAirDate});
- }
- print $FH "\t\t$date\n" if defined($date);
-
- if (defined($programs{$p}{genres})) {
- foreach my $g (sort { $programs{$p}{genres}{$a} <=> $programs{$p}{genres}{$b} or $a cmp $b } keys %{$programs{$p}{genres}} ) {
- print $FH "\t\t" . &enc(ucfirst($g)) . "\n";
- }
- }
-
- print $FH "\t\t" . $programs{$p}{duration} . "\n" if defined($programs{$p}{duration});
-
- if (defined($programs{$p}{imageUrl})) {
- print $FH "\t\t\n";
- }
-
- if (defined($programs{$p}{url})) {
- print $FH "\t\t" . &enc($programs{$p}{url}) . "\n";
- }
-
- my $xs;
- my $xe;
-
- if (defined($programs{$p}{seasonNum}) && defined($programs{$p}{episodeNum})) {
- my $s = $programs{$p}{seasonNum};
- my $sf = sprintf("S%0*d", &max(2, length($s)), $s);
- my $e = $programs{$p}{episodeNum};
- my $ef = sprintf("E%0*d", &max(2, length($e)), $e);
-
- $xs = int($s) - 1;
- $xe = int($e) - 1;
-
- if ($s > 0 || $e > 0) {
- print $FH "\t\t" . $sf . $ef . "\n";
- }
- }
-
- $dd_prog_id = $p;
- if ( $dd_prog_id =~ /^(..\d{8})(\d{4})/ ) {
- $dd_prog_id = sprintf("%s.%s",$1,$2);
- print $FH "\t\t" . $dd_prog_id . "\n";
- }
-
- if (defined($xs) && defined($xe) && $xs >= 0 && $xe >= 0) {
- print $FH "\t\t" . $xs . "." . $xe . ".\n";
- }
-
- if (defined($schedule{$station}{$s}{quality})) {
- print $FH "\t\t\n";
- }
- my $new = defined($schedule{$station}{$s}{new});
- my $live = defined($schedule{$station}{$s}{live});
- my $cc = defined($schedule{$station}{$s}{cc});
-
- if (! $new && ! $live && $p =~ /^EP|^SH|^\d/) {
- print $FH "\t\t\n";
- }
-
- if (defined($schedule{$station}{$s}{premiere})) {
- print $FH "\t\t" . $schedule{$station}{$s}{premiere} . "\n";
- }
-
- if (defined($schedule{$station}{$s}{finale})) {
- print $FH "\t\t" . $schedule{$station}{$s}{finale} . "\n";
- }
-
- print $FH "\t\t\n" if $new;
- # not part of XMLTV format yet?
- print $FH "\t\t\n" if (defined($options{L}) && $live);
- print $FH "\t\t\n" if $cc;
-
- if (defined($programs{$p}{rating})) {
- print $FH "\t\t\n\t\t\t" . $programs{$p}{rating} . "\n\t\t\n"
- }
-
- if (defined($programs{$p}{starRating})) {
- print $FH "\t\t\n\t\t\t" . $programs{$p}{starRating} . "/4\n\t\t\n";
- }
- print $FH "\t\n";
- $i++;
- }
- }
-}
-
-sub printHeaderXTVD {
- my ($FH, $enc) = @_;
- print $FH "\n";
- print $FH "\n";
-}
-
-sub printCredits {
- my ($FH, $p, $s) = @_;
- foreach my $g (sort { $programs{$p}{$s}{$a} <=> $programs{$p}{$s}{$b} } keys %{$programs{$p}{$s}} ) {
- print $FH "\t\t\t<$s>" . &enc($g) . "$s>\n";
- }
-}
-
-sub printFooterXTVD {
- my $FH = shift;
- print $FH "\n";
-}
-
-sub printStationsXTVD {
- my $FH = shift;
- print $FH "\n";
- for my $key ( sort sortChan keys %stations ) {
- print $FH "\t\n";
- if (defined($stations{$key}{number})) {
- $sname = &enc($stations{$key}{name});
- print $FH "\t\t" . $sname . "\n";
- print $FH "\t\t" . $sname . "\n";
- print $FH "\t\t" . $stations{$key}{number} . "\n";
- ©Logo($key);
- }
- print $FH "\t\n";
- }
- print $FH "\n";
-}
-
-sub printLineupsXTVD {
- my $FH = shift;
- print $FH "\n";
- print $FH "\t\n";
- for my $key ( sort sortChan keys %stations ) {
- if (defined($stations{$key}{number})) {
- print $FH "\t\n";
- }
- }
- print $FH "\t\n";
- print $FH "\n";
-}
-
-sub printSchedulesXTVD {
- my $FH = shift;
- print $FH "\n";
- for my $station ( sort sortChan keys %stations ) {
- my $i = 0;
- my @keyArray = sort { $schedule{$station}{$a}{time} cmp $schedule{$station}{$b}{time} } keys %{$schedule{$station}};
- foreach $s (@keyArray) {
- if ($#keyArray <= $i) {
- delete $schedule{$station}{$s};
- next;
- }
- my $p = $schedule{$station}{$s}{program};
- my $startTime = &convTimeXTVD($schedule{$station}{$s}{time});
- my $stopTime = &convTimeXTVD($schedule{$station}{$keyArray[$i+1]}{time});
- my $duration = &convDurationXTVD($schedule{$station}{$keyArray[$i+1]}{time} - $schedule{$station}{$s}{time});
-
- print $FH "\t\n";
- $i++;
- }
- }
- print $FH "\n";
-}
-
-sub printProgramsXTVD {
- my $FH = shift;
- print $FH "\n";
- foreach $p (keys %programs) {
- print $FH "\t\n";
- print $FH "\t\t" . &enc($programs{$p}{title}) . "\n" if defined($programs{$p}{title});
- print $FH "\t\t" . &enc($programs{$p}{episode}) . "\n" if defined($programs{$p}{episode});
- print $FH "\t\t" . &enc($programs{$p}{description}) . "\n" if defined($programs{$p}{description});
-
- if (defined($programs{$p}{movie_year})) {
- print $FH "\t\t" . $programs{$p}{movie_year} . "\n";
- } else { #Guess
- my $showType = "Series";
- if ($programs{$p}{title} =~ /Paid Programming/i) {
- $showType = "Paid Programming";
- }
- print $FH "\t\t$showType\n";
- print $FH "\t\tEP" . substr($p,2,8) . "\n";
- print $FH "\t\t" . &convOADXTVD($programs{$p}{originalAirDate}) . "\n" if defined($programs{$p}{originalAirDate});
- }
- print $FH "\t\n";
- }
- print $FH "\n";
-}
-
-sub printGenresXTVD {
- my $FH = shift;
- print $FH "\n";
- foreach $p (keys %programs) {
- if (defined($programs{$p}{genres}) && $programs{$p}{genres}{movie} != 1) {
- print $FH "\t\n";
- foreach my $g (keys %{$programs{$p}{genres}}) {
- print $FH "\t\t\n";
- print $FH "\t\t\t" . &enc(ucfirst($g)) . "\n";
- print $FH "\t\t\t0\n";
- print $FH "\t\t\n";
- }
- print $FH "\t\n";
- }
- }
- print $FH "\n";
-}
-
-sub loginTVG {
- my $r = &ua_get($tvgurl . 'signin/');
- if ($r->is_success) {
- my $str = $r->decoded_content;
- if ($str =~ / $token,
- email => $userEmail,
- password => $password,
- }, 'X-Requested-With' => 'XMLHttpRequest'
- );
-
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($dc =~ /success/) {
- $ua->cookie_jar->scan(sub { if ($_[1] eq "ServiceID") { $zlineupId = $_[2]; }; });
- if (!defined($options{a})) {
- my $r = &ua_get($tvgurl . "user/favorites/?provider=$zlineupId",'X-Requested-With' => 'XMLHttpRequest');
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($dc =~ /\{\"code\":200/) {
- &parseTVGFavs($dc);
- }
- }
- return $dc;
- } else {
- &pout("[Attempt $rc] " . $r->status_line . ":" . $dc . "\n");
- sleep ($sleeptime + 1);
- }
- }
- die "Failed to login within $retries retries.\n";
- }
- } else {
- die "Login token not found\n";
- }
- }
-}
-
-sub loginZAP {
- my $rc = 0;
- while ($rc++ < $retries) {
- my $r = &ua_post($urlRoot . 'api/user/login',
- {
- emailid => $userEmail, password => $password,
- usertype => '0', facebookuser =>'false',
- }
- );
-
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($r->is_success) {
- my $t = decode_json($dc);
- $zapToken = $t->{'token'};
- $zapPref = '';
- $zapPref .= "m" if ($t->{isMusic});
- $zapPref .= "p" if ($t->{isPPV});
- $zapPref .= "h" if ($t->{isHD});
- if ($zapPref eq '') { $zapPref = '-' }
- else {
- $zapPref = join(",", split(//, $zapPref));
- }
-
- my $prs = $t->{'properties'};
- $postalcode = $prs->{2002};
- $country = $prs->{2003};
- ($lineupId, $device) = split(/:/, $prs->{2004});
- if (!defined($options{a})) {
- my $r = &ua_post($urlRoot . "api/user/favorites", { token => $zapToken }, 'X-Requested-With' => 'XMLHttpRequest');
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- if ($r->is_success) {
- &parseZFavs($dc);
- } else {
- &perr("FF" . $r->status_line . ": $dc\n");
- }
- }
- return $dc;
- } else {
- &pout("[Attempt $rc] " . $dc . "\n");
- sleep ($sleeptime + 1);
- }
- }
- die "Failed to login within $retries retries.\n";
-}
-
-sub getZapGParams {
- my %hash = &getZapParams();
- $hash{country} = delete $hash{countryCode};
- return join("&", map { "$_=$hash{$_}" } keys %hash);
-}
-
-sub getZapPParams {
- my %hash = &getZapParams();
- delete $hash{lineupId};
- return %hash;
-}
-
-sub getZapParams {
- my %phash = ();
- if (defined($zlineupId) || defined($zipcode)) {
- $postalcode = $zipcode;
- $country = "USA";
- $country = "CAN" if ($zipcode =~ /[A-z]/);
- if ($zlineupId =~ /:/) {
- ($lineupId, $device) = split(/:/, $zlineupId);
- } else {
- $lineupId = $zlineupId;
- $device = "-";
- }
- $phash{postalCode} = $postalcode;
- } else {
- $phash{token} = &getZToken();
- }
- $phash{lineupId} = "$country-$lineupId-DEFAULT";
- $phash{postalCode} = $postalcode;
- $phash{countryCode} = $country;
- $phash{headendId} = $lineupId;
- $phash{device} = $device;
- $phash{aid} = 'lat';
- return %phash;
-}
-
-sub login {
- if (!defined($userEmail) || $userEmail eq '' || !defined($password) || $password eq '') {
- if (!defined($zlineupId)) {
- die "Unable to login: Unspecified username or password.\n"
- }
- }
-
- if (!defined($ua)) {
- $ua = LWP::UserAgent->new(ssl_opts => { verify_hostname => 0 }); # WIN
- $ua->conn_cache(LWP::ConnCache->new( total_capacity => undef ));
- $ua->cookie_jar(HTTP::Cookies->new);
- $ua->proxy(['http', 'https'], $proxy) if defined($proxy);
- $ua->agent($userAgent);
- $ua->default_headers->push_header('Accept-Encoding' => 'gzip, deflate');
- }
-
- if ($userEmail ne '' && $password ne '') {
- &pout("Logging in as \"$userEmail\" (" . localtime . ")\n");
- if (defined($options{z})) {
- &loginTVG();
- } else {
- &loginZAP();
- }
- } else {
- &pout("Connecting with lineupId \"$zlineupId\" (" . localtime . ")\n");
- }
-}
-
-sub ua_stats {
- my ($s, @p) = @_;
- my $r;
- if ($s eq 'POST') {
- $r = $ua->post(@p);
- } else {
- $r = $ua->get(@p);
- }
- my $cc = $ua->conn_cache;
- if (defined($cc)) {
- my @cxns = $cc->get_connections();
- foreach (@cxns) {
- $tsocks{$_} = 1;
- }
- }
- $treq++;
- $tb += length($r->content);
- return $r;
-}
-
-sub ua_get { return &ua_stats('GET', @_); }
-sub ua_post { return &ua_stats('POST', @_); }
-
-sub getURL {
- my $url = shift;
- my $er = shift;
- &login() if !defined($ua);
-
- my $rc = 0;
- while ($rc++ < $retries) {
- &pout("[$treq] Getting: $url\n");
- sleep $sleeptime; # do these rapid requests flood servers?
- my $r = &ua_get($url);
- my $cl = length($r->content);
- my $dc = $r->decoded_content( raise_error => 1 );
- if ($r->is_success && $cl) {
- return $dc;
- } elsif ($r->code == 500 && $dc =~ /Could not load details/) {
- &pout("$dc\n");
- return "";
- } else {
- &perr("[Attempt $rc] $cl:" . $r->status_line . "\n");
- &perr($r->content . "\n");
- sleep ($sleeptime + 2);
- }
- }
- &perr("Failed to download within $retries retries.\n");
- if ($er) {
- &perr("Server out of data? Temporary server error? Normal exit anyway.\n");
- return "";
- };
- die;
-}
-
-sub wbf {
- my($f, $s) = @_;
- open(FO, ">$f") or die "Failed to open '$f': $!";
- binmode(FO);
- print FO $s;
- close(FO);
-}
-
-sub unf {
- my $f = shift;
- unlink($f) or &perr("Failed to delete '$f': $!");
-}
-
-sub copyLogo {
- my $key = shift;
- my $cid = $key;
- if (!defined($logos{$cid}{logo})) {
- $cid = substr($key, rindex($key, ".")+1);
- }
- if (defined($iconDir) && defined($logos{$cid}{logo})) {
- my $num = $stations{$key}{number};
- my $src = "$iconDir/" . $logos{$cid}{logo} . $logos{$cid}{logoExt};
- my $dest1 = "$iconDir/$num" . $logos{$cid}{logoExt};
- my $dest2 = "$iconDir/$num " . $stations{$key}{name} . $logos{$cid}{logoExt};
- my $dest3 = "$iconDir/$num\_" . $stations{$key}{name} . $logos{$cid}{logoExt};
- copy($src, $dest1);
- copy($src, $dest2);
- copy($src, $dest3);
- }
-}
-
-sub handleLogo {
- my $url = shift;
- if (! -d $iconDir) {
- mkdir($iconDir) or die "Can't mkdir: $!\n";
- }
- my $n; my $s; ($n,$_,$s) = fileparse($url, qr"\..*");
- $stations{$cs}{logoURL} = $url;
- $logos{$cs}{logo} = $n;
- $logos{$cs}{logoExt} = $s;
- my $f = $iconDir . "/" . $n . $s;
- if (! -e $f) { &wbf($f, &getURL($url,0)); }
-}
-
-sub setOriginalAirDate {
- if (substr($cp,10,4) ne '0000') {
- if (!defined($programs{$cp}{originalAirDate})
- || ($schedule{$cs}{$sch}{time} < $programs{$cp}{originalAirDate})) {
- $programs{$cp}{originalAirDate} = $schedule{$cs}{$sch}{time};
- }
- }
-}
-
-sub getZToken {
- &login() if (!defined($zapToken));
- return $zapToken;
-}
-
-sub parseZFavs {
- my $buffer = shift;
- my $t = decode_json($buffer);
- if (defined($t->{'channels'})) {
- my $m = $t->{'channels'};
- foreach my $f (@{$m}) {
- if ($options{R}) {
- my $r = &ua_post($urlRoot . "api/user/ChannelAddtofav", { token => $zapToken, prgsvcid => $f, addToFav => "false" }, 'X-Requested-With' => 'XMLHttpRequest');
- if ($r->is_success) {
- &pout("Removed favorite $f\n");
- } else {
- &perr("RF" . $r->status_line . "\n");
- }
- } else {
- $zapFavorites{$f} = 1;
- }
- }
- if ($options{R}) {
- &pout("Removed favorites, exiting\n");
- exit;
- };
- &pout("Lineup favorites: " . (keys %zapFavorites) . "\n");
- }
-}
-
-sub parseTVGFavs {
- my $buffer = shift;
- my $t = decode_json($buffer);
- if (defined($t->{'message'})) {
- my $m = $t->{'message'};
- foreach my $f (@{$m}) {
- my $source = $f->{"source"};
- my $channel = $f->{"channel"};
- $tvgfavs{"$channel.$source"} = 1;
- }
- &pout("Lineup $zlineupId favorites: " . (keys %tvgfavs) . "\n");
- }
-}
-
-sub parseTVGIcons {
- require GD;
- $rc = Encode::encode('utf8', &getURL($tvgspritesurl . "$zlineupId\.css",0) );
- if ($rc =~ /background-image:.+?url\((.+?)\)/) {
- my $url = $tvgspritesurl . $1;
-
- if (! -d $iconDir) {
- mkdir($iconDir) or die "Can't mkdir: $!\n";
- }
-
- ($n,$_,$s) = fileparse($url, qr"\..*");
- $f = $iconDir . "/sprites-" . $n . $s;
- &wbf($f, &getURL($url,0));
-
- GD::Image->trueColor(1);
- $im = new GD::Image->new($f);
-
- my $iconw = 30;
- my $iconh = 20;
- while ($rc =~ /listings-channel-icon-(.+?)\{.+?position:.*?\-(\d+).+?(\d+).*?\}/isg) {
- my $cid = $1;
- my $iconx = $2;
- my $icony = $3;
-
- my $icon = new GD::Image($iconw,$iconh);
- $icon->alphaBlending(0);
- $icon->saveAlpha(1);
- $icon->copy($im, 0, 0, $iconx, $icony, $iconw, $iconh);
-
- $logos{$cid}{logo} = "sprite-" . $cid;
- $logos{$cid}{logoExt} = $s;
-
- my $ifn = $iconDir . "/" . $logos{$cid}{logo} . $logos{$cid}{logoExt};
- &wbf($ifn, $icon->png);
- }
- }
-}
-
-sub parseTVGD {
- my $gz = gzopen(shift, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
-
- if (defined($t->{'program'})) {
- my $prog = $t->{'program'};
- if (defined($prog->{'release_year'})) {
- $programs{$cp}{movie_year} = $prog->{'release_year'};
- }
- if (defined($prog->{'rating'}) && !defined($programs{$cp}{rating})) {
- $programs{$cp}{rating} = $prog->{'rating'} if $prog->{'rating'} ne 'NR';
- }
- }
-
- if (defined($t->{'tvobject'})) {
- my $tvo = $t->{'tvobject'};
- if (defined($tvo->{'photos'})) {
- my $photos = $tvo->{'photos'};
- my %phash;
- foreach $ph (@{$photos}) {
- my $w = $ph->{'width'} * $ph->{'height'};
- my $u = $ph->{'url'};
- $phash{$w} = $u;
- }
- my $big = (sort {$b <=> $a} keys %phash)[0];
- $programs{$cp}{imageUrl} = $phash{$big};
- }
- }
-}
-
-sub parseTVGGrid {
- my $gz = gzopen(shift, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
-
- foreach my $e (@{$t}) {
- my $cjs = $e->{'Channel'};
- my $src = $cjs->{'SourceId'};
- my $num = $cjs->{'Number'};
-
- $cs = "$num.$src";
-
- if (%tvgfavs) {
- next if (!$tvgfavs{$cs});
- }
-
- if (!defined($stations{$cs}{stnNum})) {
- $stations{$cs}{stnNum} = $src;
- $stations{$cs}{number} = $num;
- $stations{$cs}{name} = $cjs->{'Name'};
- if (defined($cjs->{'FullName'}) && $cjs->{'FullName'} ne $cjs->{'Name'}) {
- if ($cjs->{'FullName'} ne '') {
- $stations{$cs}{fullname} = $cjs->{'FullName'};
- }
- }
-
- if (!defined($stations{$cs}{order})) {
- if (defined($options{b})) {
- $stations{$cs}{order} = $coNum++;
- } else {
- $stations{$cs}{order} = $stations{$cs}{number};
- }
- }
- }
-
- my $cps = $e->{'ProgramSchedules'};
- foreach my $pe (@{$cps}) {
- next if (!defined($pe->{'ProgramId'}));
- $cp = $pe->{'ProgramId'};
- my $catid = $pe->{'CatId'};
-
- if ($catid == 1) { $programs{$cp}{genres}{movie} = 1 }
- elsif ($catid == 2) { $programs{$cp}{genres}{sports} = 1 }
- elsif ($catid == 3) { $programs{$cp}{genres}{family} = 1 }
- elsif ($catid == 4) { $programs{$cp}{genres}{news} = 1 }
- # 5 - 10?
- # my $subcatid = $pe->{'SubCatId'};
-
- my $ppid = $pe->{'ParentProgramId'};
- if ((defined($ppid) && $ppid != 0)
- || (defined($options{j}) && $catid != 1)) {
- $programs{$cp}{genres}{series} = 99;
- }
-
- $programs{$cp}{title} = $pe->{'Title'};
- $tba = 1 if $programs{$cp}{title} =~ /$sTBA/i;
-
- if (defined($pe->{'EpisodeTitle'}) && $pe->{'EpisodeTitle'} ne '') {
- $programs{$cp}{episode} = $pe->{'EpisodeTitle'};
- $tba = 1 if $programs{$cp}{episode} =~ /$sTBA/i;
- }
-
- $programs{$cp}{description} = $pe->{'CopyText'} if defined($pe->{'CopyText'}) && $pe->{'CopyText'} ne '';
- $programs{$cp}{rating} = $pe->{'Rating'} if defined($pe->{'Rating'}) && $pe->{'Rating'} ne '';
-
- my $sch = $pe->{'StartTime'} * 1000;
- $schedule{$cs}{$sch}{time} = $sch;
- $schedule{$cs}{$sch}{endtime} = $pe->{'EndTime'} * 1000;
- $schedule{$cs}{$sch}{program} = $cp;
- $schedule{$cs}{$sch}{station} = $cs;
-
- my $airat = $pe->{'AiringAttrib'};
- if ($airat & 1) { $schedule{$cs}{$sch}{live} = 1 }
- elsif ($airat & 4) { $schedule{$cs}{$sch}{new} = 1 }
- # other bits?
-
- my $tvo = $pe->{'TVObject'};
- if (defined($tvo)) {
- if (defined($tvo->{'SeasonNumber'}) && $tvo->{'SeasonNumber'} != 0) {
- $programs{$cp}{seasonNum} = $tvo->{'SeasonNumber'};
- if (defined($tvo->{'EpisodeNumber'}) && $tvo->{'EpisodeNumber'} != 0) {
- $programs{$cp}{episodeNum} = $tvo->{'EpisodeNumber'};
- }
- }
- if (defined($tvo->{'EpisodeAirDate'})) {
- my $eaid = $tvo->{'EpisodeAirDate'}; # GMT @ 00:00:00
- $eaid =~ tr/\-0-9//cd;
- $programs{$cp}{originalAirDate} = $eaid if ($eaid ne '');
- }
- my $url;
- if (defined($tvo->{'EpisodeSEOUrl'}) && $tvo->{'EpisodeSEOUrl'} ne '') {
- $url = $tvo->{'EpisodeSEOUrl'};
- } elsif(defined($tvo->{'SEOUrl'}) && $tvo->{'SEOUrl'} ne '') {
- $url = $tvo->{'SEOUrl'};
- $url = "/movies$url" if ($catid == 1 && $url !~ /movies/);
- }
- $programs{$cp}{url} = substr($tvgurl, 0, -1) . $url if defined($url);
- }
-
- if (defined($options{I})
- || (defined($options{D}) && $programs{$cp}{genres}{movie})
- || (defined($options{W}) && $programs{$cp}{genres}{movie}) ) {
- &getDetails(\&parseTVGD, $cp, $tvgMapiRoot . "listings/details?program=$cp", "");
- }
- }
- }
-}
-
-sub getDetails {
- my ($func, $cp, $url, $prefix) = @_;
- my $fn = "$cacheDir/$prefix$cp\.js\.gz";
- if (! -e $fn) {
- my $rs = &getURL($url,1);
- if (length($rs)) {
- $rc = Encode::encode('utf8', $rs);
- &wbf($fn, Compress::Zlib::memGzip($rc));
- }
- }
- if (-e $fn) {
- my $l = length($prefix) ? $prefix : "D";
- &pout("[$l] Parsing: $cp\n");
- $func->($fn);
- } else {
- &pout("Skipping: $cp\n");
- }
-}
-
-sub parseJSON {
- my $gz = gzopen(shift, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
-
- my $sts = $t->{'channels'};
- my %zapStarred=();
- foreach $s (@$sts) {
-
- if (defined($s->{'channelId'})) {
- if (!$allChan && scalar(keys %zapFavorites)) {
- if ($zapFavorites{$s->{channelId}}) {
- if ($options{8}) {
- next if $zapStarred{$s->{channelId}};
- $zapStarred{$s->{channelId}} = 1;
- }
- } else {
- next;
- }
- }
- # id (uniq) vs channelId, but id not nec consistent in cache
- $cs = $s->{channelNo} . "." . $s->{channelId};
- $stations{$cs}{stnNum} = $s->{channelId};
- $stations{$cs}{name} = $s->{'callSign'};
- $stations{$cs}{number} = $s->{'channelNo'};
- $stations{$cs}{number} =~ s/^0+//g;
-
- if (!defined($stations{$cs}{order})) {
- if (defined($options{b})) {
- $stations{$cs}{order} = $coNum++;
- } else {
- $stations{$cs}{order} = $stations{$cs}{number};
- }
- }
-
- if ($s->{'thumbnail'} ne '') {
- my $url = $s->{'thumbnail'};
- $url =~ s/\?.*//; # remove size
- if ($url !~ /^http/) {
- $url = "https:" . $url;
- }
- $stations{$cs}{logoURL} = $url;
- &handleLogo($url) if defined($iconDir);
- }
-
- my $events = $s->{'events'};
- foreach $e (@$events) {
- my $program = $e->{'program'};
- $cp = $program->{'id'};
- $programs{$cp}{title} = $program->{'title'};
- $tba = 1 if $programs{$cp}{title} =~ /$sTBA/i;
- $programs{$cp}{episode} = $program->{'episodeTitle'} if ($program->{'episodeTitle'} ne '');
- $programs{$cp}{description} = $program->{'shortDesc'} if ($program->{'shortDesc'} ne '');
- $programs{$cp}{duration} = $e->{duration} if ($e->{duration} > 0);
- $programs{$cp}{movie_year} = $program->{releaseYear} if ($program->{releaseYear} ne '');
- $programs{$cp}{seasonNum} = $program->{season} if ($program->{'season'} ne '');
- if ($program->{'episode'} ne '') {
- $programs{$cp}{episodeNum} = $program->{episode};
- }
- if ($e->{'thumbnail'} ne '') {
- my $turl = $urlAssets;
- $turl .= $e->{'thumbnail'} . ".jpg";
- $programs{$cp}{imageUrl} = $turl;
- }
- if ($program->{'seriesId'} ne '' && $program->{'tmsId'} ne '') {
- $programs{$cp}{url} = $urlRoot . "overview-affiliates.html?programSeriesId="
- . $program->{seriesId} . "&tmsId=" . $program->{tmsId};
- }
-
- $sch = str2time1($e->{'startTime'}) * 1000;
- $schedule{$cs}{$sch}{time} = $sch;
- $schedule{$cs}{$sch}{endTime} = str2time1($e->{'endTime'}) * 1000;
- $schedule{$cs}{$sch}{program} = $cp;
- $schedule{$cs}{$sch}{station} = $cs;
-
- if ($e->{'filter'}) {
- my $genres = $e->{'filter'};
- my $i = 1;
- foreach $g (@{$genres}) {
- $g =~ s/filter-//i;
- ${$programs{$cp}{genres}}{lc($g)} = $i++;
- }
- }
-
- $programs{$cp}{rating} = $e->{rating} if ($e->{rating} ne '');
-
- if ($e->{'tags'}) {
- my $tags = $e->{'tags'};
- if (grep $_ eq 'CC', @{$tags}) {
- $schedule{$cs}{$sch}{cc} = 1
- }
- }
-
- if ($e->{'flag'}) {
- my $flags = $e->{'flag'};
- if (grep $_ eq 'New', @{$flags}) {
- $schedule{$cs}{$sch}{new} = 'New'
- &setOriginalAirDate();
- }
- if (grep $_ eq 'Live', @{$flags}) {
- $schedule{$cs}{$sch}{live} = 'Live'
- &setOriginalAirDate(); # live to tape?
- }
- if (grep $_ eq 'Premiere', @{$flags}) {
- $schedule{$cs}{$sch}{premiere} = 'Premiere';
- }
- if (grep $_ eq 'Finale', @{$flags}) {
- $schedule{$cs}{$sch}{finale} = 'Finale';
- }
- }
-
- if ($options{D} && !$program->{isGeneric}) {
- &postJSONO($cp, $program->{seriesId});
- }
- if (defined($options{j}) && $cp !~ /^MV/) {
- $programs{$cp}{genres}{series} = 99;
- }
- }
- }
- }
- return 0;
-}
-
-sub postJSONO {
- my ($cp, $sid) = @_;
- my $fn = "$cacheDir/O$cp\.js\.gz";
-
- if (! -e $fn && defined($sidCache{$sid}) && -e $sidCache{$sid}) {
- copy($sidCache{$sid}, $fn);
- }
- if (! -e $fn) {
- my $url = $urlRoot . 'api/program/overviewDetails';
- &pout("[$treq] Post $sid: $url\n");
- sleep $sleeptime; # do these rapid requests flood servers?
- my %phash = &getZapPParams();
- $phash{programSeriesID} = $sid;
- $phash{'clickstream[FromPage]'} = 'TV%20Grid';
- my $r = &ua_post($url, \%phash, 'X-Requested-With' => 'XMLHttpRequest');
- if ($r->is_success) {
- $dc = Encode::encode('utf8', $r->decoded_content( raise_error => 1 ));
- &wbf($fn, Compress::Zlib::memGzip($dc));
- $sidCache{$sid} = $fn;
- } else {
- &perr($id . " :" . $r->status_line);
- }
- }
- if (-e $fn) {
- &pout("[D] Parsing: $cp\n");
- my $gz = gzopen($fn, "rb");
- my $buffer;
- $buffer .= $b while $gz->gzread($b, 65535) > 0;
- $gz->gzclose();
- my $t = decode_json($buffer);
-
- if ($t->{seriesGenres} ne '') {
- my $i = 2;
- my %gh = %{$programs{$cp}{genres}};
- if (keys %gh) {
- my @genArr = sort { $gh{$a} <=> $gh{$b} } keys %gh;
- my $max = $genArr[-1];
- $i = $gh{$max} + 1;
- }
- foreach my $sg (split(/\|/, lc($t->{seriesGenres}))) {
- if (!${$programs{$cp}{genres}}{$sg}) {
- ${$programs{$cp}{genres}}{$sg} = $i++;
- }
- }
- }
-
- my $i = 1;
- foreach my $c (@{$t->{'overviewTab'}->{cast}}) {
- my $n = $c->{name};
- my $cn = $c->{characterName};
- my $cr = lc($c->{role});
-
- if ($cr eq 'host') {
- ${$programs{$cp}{presenter}}{$n} = $i++;
- } else {
- ${$programs{$cp}{actor}}{$n} = $i++;
- ${$programs{$cp}{role}}{$n} = $cn if length($cn);
- }
- }
- $i = 1;
- foreach my $c (@{$t->{'overviewTab'}->{crew}}) {
- my $n = $c->{name};
- my $cr = lc($c->{role});
- if ($cr =~ /producer/) {
- ${$programs{$cp}{producer}}{$n} = $i++;
- } elsif ($cr =~ /director/) {
- ${$programs{$cp}{director}}{$n} = $i++;
- } elsif ($cr =~ /writer/) {
- ${$programs{$cp}{writer}}{$n} = $i++;
- }
- }
- if (!defined($programs{$cp}{imageUrl}) && $t->{seriesImage} ne '') {
- my $turl = $urlAssets;
- $turl .= $t->{seriesImage} . ".jpg";
- $programs{$cp}{imageUrl} = $turl;
- }
- if ($cp =~ /^MV|^SH/ && length($t->{seriesDescription}) > length($programs{$cp}{description})) {
- $programs{$cp}{description} = $t->{seriesDescription};
- }
- if ($cp =~ /^EP/) { # GMT @ 00:00:00
- my $ue = $t->{overviewTab}->{upcomingEpisode};
- if (defined($ue) && lc($ue->{tmsID}) eq lc($cp)
- && $ue->{originalAirDate} ne ''
- && $ue->{originalAirDate} ne '1000-01-01T00:00Z') {
- $oad = str2time2($ue->{originalAirDate}) ;
- $oad *= 1000;
- $programs{$cp}{originalAirDate} = $oad;
- } else {
- foreach my $ue (@{$t->{upcomingEpisodeTab}}) {
- if (lc($ue->{tmsID}) eq lc($cp)
- && $ue->{originalAirDate} ne ''
- && $ue->{originalAirDate} ne '1000-01-01T00:00Z'
- ) {
- $oad = str2time2($ue->{originalAirDate}) ;
- $oad *= 1000;
- $programs{$cp}{originalAirDate} = $oad;
- last;
- }
- }
- }
- }
- } else {
- &pout("Skipping: $sid\n");
- }
-}
-
-sub str2time1 {
- my $t = Time::Piece->strptime(shift, '%Y-%m-%dT%H:%M:%SZ');
- return $t->epoch();
-}
-
-sub str2time2 {
- my $t = Time::Piece->strptime(shift, '%Y-%m-%dT%H:%MZ');
- return $t->epoch();
-}
-
-sub hourToMillis {
- ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
- if ($start == 0) {
- $hour = int($hour/$gridHours) * $gridHours;
- } else {
- $hour = 0;
- }
- $t = timegm(0,0,$hour,$mday,$mon,$year);
- $t = $t - (&tz_offset * 3600) if !defined($options{g});
- ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = gmtime($t);
- $t = timegm($sec, $min, $hour,$mday,$mon,$year);
- return $t . "000";
-}
-
-sub tz_offset {
- my $n = defined $_[0] ? $_[0] : time;
- my ($lm, $lh, $ly, $lyd) = (localtime $n)[1, 2, 5, 7];
- my ($gm, $gh, $gy, $gyd) = (gmtime $n)[1, 2, 5, 7];
- ($lm - $gm)/60 + $lh - $gh + 24 * ($ly - $gy || $lyd - $gyd)
-}
-
-sub timezone {
- my $tztime = defined $_[0] ? &_rtrim3(shift) : time;
- my $os = sprintf "%.1f", (timegm(localtime($tztime)) - $tztime) / 3600;
- my $mins = sprintf "%02d", abs( $os - int($os) ) * 60;
- return sprintf("%+03d", int($os)) . $mins;
-}
-
-sub max ($$) { $_[$_[0] < $_[1]] }
-sub min ($$) { $_[$_[0] > $_[1]] }
-
-sub HELP_MESSAGE {
-print < ($VERSION)
- -u
- -p
- -d <# of days> (default = $days)
- -n <# of no-cache days> (from end) (default = $ncdays)
- -N <# of no-cache days> (from start) (default = $ncsdays)
- -B
- -s (default = $start)
- -o