diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 6aada44..cc2949a 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -172,10 +172,6 @@ jobs:
target: aarch64-unknown-linux-gnu
bun_target: bun-linux-arm64
artifact_name: operator-linux-arm64
- - os: macos-15-intel
- target: x86_64-apple-darwin
- bun_target: bun-darwin-x64
- artifact_name: operator-macos-x86_64
- os: macos-14
target: aarch64-apple-darwin
bun_target: bun-darwin-arm64
@@ -184,10 +180,6 @@ jobs:
target: x86_64-pc-windows-msvc
bun_target: bun-windows-x64
artifact_name: operator-windows-x86_64.exe
- - os: windows-11-arm
- target: aarch64-pc-windows-msvc
- bun_target: bun-windows-arm64
- artifact_name: operator-windows-arm64.exe
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
diff --git a/.github/workflows/opr8r.yaml b/.github/workflows/opr8r.yaml
index 9707359..0be9696 100644
--- a/.github/workflows/opr8r.yaml
+++ b/.github/workflows/opr8r.yaml
@@ -106,9 +106,6 @@ jobs:
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04-arm
artifact: opr8r-linux-arm64
- - target: x86_64-apple-darwin
- os: macos-13
- artifact: opr8r-macos-x86_64
- target: aarch64-apple-darwin
os: macos-14
artifact: opr8r-macos-arm64
@@ -116,11 +113,6 @@ jobs:
os: windows-latest
artifact: opr8r-windows-x86_64
extension: .exe
- - target: aarch64-pc-windows-msvc
- os: windows-latest
- artifact: opr8r-windows-arm64
- extension: .exe
- cross_compile: true
runs-on: ${{ matrix.os }}
defaults:
run:
@@ -142,41 +134,18 @@ jobs:
restore-keys: |
opr8r-${{ matrix.artifact }}-cargo-
- - name: Install cross-compile target (Windows ARM64)
- if: matrix.cross_compile == true
- run: rustup target add ${{ matrix.target }}
-
- name: Build release binary
- run: cargo build --release --target ${{ matrix.target }}
- if: matrix.cross_compile == true
-
- - name: Build release binary (native)
run: cargo build --release
- if: matrix.cross_compile != true
- name: Strip binary (Linux/macOS)
if: runner.os != 'Windows'
- run: |
- if [ "${{ matrix.cross_compile }}" = "true" ]; then
- strip target/${{ matrix.target }}/release/opr8r || true
- else
- strip target/release/opr8r || true
- fi
-
- - name: Upload artifact (native)
- if: matrix.cross_compile != true
- uses: actions/upload-artifact@v4
- with:
- name: ${{ matrix.artifact }}
- path: opr8r/target/release/opr8r${{ matrix.extension || '' }}
- retention-days: 30
+ run: strip target/release/opr8r || true
- - name: Upload artifact (cross-compiled)
- if: matrix.cross_compile == true
+ - name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
- path: opr8r/target/${{ matrix.target }}/release/opr8r${{ matrix.extension || '' }}
+ path: opr8r/target/release/opr8r${{ matrix.extension || '' }}
retention-days: 30
release:
@@ -228,10 +197,8 @@ jobs:
### Downloads
- **Linux x86_64**: `opr8r-linux-x86_64`
- **Linux ARM64**: `opr8r-linux-arm64`
- - **macOS Intel**: `opr8r-macos-x86_64`
- **macOS Apple Silicon**: `opr8r-macos-arm64`
- **Windows x86_64**: `opr8r-windows-x86_64.exe`
- - **Windows ARM64**: `opr8r-windows-arm64.exe`
files: release/*
draft: false
prerelease: false
diff --git a/.github/workflows/vscode-extension.yaml b/.github/workflows/vscode-extension.yaml
index b50f57e..f17e649 100644
--- a/.github/workflows/vscode-extension.yaml
+++ b/.github/workflows/vscode-extension.yaml
@@ -67,16 +67,11 @@ jobs:
opr8r_artifact: opr8r-linux-x86_64
- vscode_target: linux-arm64
opr8r_artifact: opr8r-linux-arm64
- - vscode_target: darwin-x64
- opr8r_artifact: opr8r-macos-x86_64
- vscode_target: darwin-arm64
opr8r_artifact: opr8r-macos-arm64
- vscode_target: win32-x64
opr8r_artifact: opr8r-windows-x86_64
opr8r_extension: .exe
- - vscode_target: win32-arm64
- opr8r_artifact: opr8r-windows-arm64
- opr8r_extension: .exe
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
diff --git a/README.md b/README.md
index 6528fb5..d78e5df 100644
--- a/README.md
+++ b/README.md
@@ -78,11 +78,6 @@ curl -L https://github.com/untra/operator/releases/latest/download/operator-maco
chmod +x operator
sudo mv operator /usr/local/bin/
-# macOS Intel
-curl -L https://github.com/untra/operator/releases/latest/download/operator-macos-x86_64 -o operator
-chmod +x operator
-sudo mv operator /usr/local/bin/
-
# Linux x86_64
curl -L https://github.com/untra/operator/releases/latest/download/operator-linux-x86_64 -o operator
chmod +x operator
diff --git a/docs/downloads/index.md b/docs/downloads/index.md
index 966b650..571ce19 100644
--- a/docs/downloads/index.md
+++ b/docs/downloads/index.md
@@ -19,11 +19,9 @@ Download Operator! for your platform. Curren
| Platform | Architecture | Download |
|----------|--------------|----------|
| macOS | ARM64 (Apple Silicon) | [operator-macos-arm64]({{ site.github.repo }}/releases/download/v{{ site.version }}/operator-macos-arm64) |
-| macOS | x86_64 (Intel) | [operator-macos-x86_64]({{ site.github.repo }}/releases/download/v{{ site.version }}/operator-macos-x86_64) |
| Linux | ARM64 | [operator-linux-arm64]({{ site.github.repo }}/releases/download/v{{ site.version }}/operator-linux-arm64) |
| Linux | x86_64 | [operator-linux-x86_64]({{ site.github.repo }}/releases/download/v{{ site.version }}/operator-linux-x86_64) |
| Windows | x86_64 | [operator-windows-x86_64.exe]({{ site.github.repo }}/releases/download/v{{ site.version }}/operator-windows-x86_64.exe) |
-| Windows | ARM64 | [operator-windows-arm64.exe]({{ site.github.repo }}/releases/download/v{{ site.version }}/operator-windows-arm64.exe) |
## Backstage Server
@@ -32,7 +30,6 @@ Optional companion server for web-based project monitoring dashboard.
| Platform | Architecture | Download |
|----------|--------------|----------|
| macOS | ARM64 | [backstage-server-bun-darwin-arm64]({{ site.github.repo }}/releases/download/v{{ site.version }}/backstage-server-bun-darwin-arm64) |
-| macOS | x64 | [backstage-server-bun-darwin-x64]({{ site.github.repo }}/releases/download/v{{ site.version }}/backstage-server-bun-darwin-x64) |
| Linux | ARM64 | [backstage-server-bun-linux-arm64]({{ site.github.repo }}/releases/download/v{{ site.version }}/backstage-server-bun-linux-arm64) |
| Linux | x64 | [backstage-server-bun-linux-x64]({{ site.github.repo }}/releases/download/v{{ site.version }}/backstage-server-bun-linux-x64) |
| Windows | x64 | [backstage-server-bun-windows-x64]({{ site.github.repo }}/releases/download/v{{ site.version }}/backstage-server-bun-windows-x64) |
diff --git a/tests/kanban_integration.rs b/tests/kanban_integration.rs
index 12abc5f..d56a8d0 100644
--- a/tests/kanban_integration.rs
+++ b/tests/kanban_integration.rs
@@ -32,20 +32,38 @@ use operator::api::providers::kanban::{
CreateIssueRequest, JiraProvider, KanbanProvider, LinearProvider, UpdateStatusRequest,
};
use std::env;
+use tokio::sync::OnceCell;
+
+// Cached credential validation results
+static JIRA_CREDENTIALS_VALID: OnceCell = OnceCell::const_new();
+static LINEAR_CREDENTIALS_VALID: OnceCell = OnceCell::const_new();
// ─── Configuration Helpers ───────────────────────────────────────────────────
-/// Check if Jira credentials are configured
+/// Check if Jira credentials are configured (non-empty env vars)
fn jira_configured() -> bool {
- env::var("OPERATOR_JIRA_DOMAIN").is_ok()
- && env::var("OPERATOR_JIRA_EMAIL").is_ok()
- && env::var("OPERATOR_JIRA_API_KEY").is_ok()
- && env::var("OPERATOR_JIRA_TEST_PROJECT").is_ok()
+ env::var("OPERATOR_JIRA_DOMAIN")
+ .map(|s| !s.is_empty())
+ .unwrap_or(false)
+ && env::var("OPERATOR_JIRA_EMAIL")
+ .map(|s| !s.is_empty())
+ .unwrap_or(false)
+ && env::var("OPERATOR_JIRA_API_KEY")
+ .map(|s| !s.is_empty())
+ .unwrap_or(false)
+ && env::var("OPERATOR_JIRA_TEST_PROJECT")
+ .map(|s| !s.is_empty())
+ .unwrap_or(false)
}
-/// Check if Linear credentials are configured
+/// Check if Linear credentials are configured (non-empty env vars)
fn linear_configured() -> bool {
- env::var("OPERATOR_LINEAR_API_KEY").is_ok() && env::var("OPERATOR_LINEAR_TEST_TEAM").is_ok()
+ env::var("OPERATOR_LINEAR_API_KEY")
+ .map(|s| !s.is_empty())
+ .unwrap_or(false)
+ && env::var("OPERATOR_LINEAR_TEST_TEAM")
+ .map(|s| !s.is_empty())
+ .unwrap_or(false)
}
/// Get the Jira test project key
@@ -79,13 +97,86 @@ fn find_terminal_status(statuses: &[String]) -> Option {
None
}
-/// Macro to skip test if provider is not configured
+/// Validate Jira credentials by testing the connection.
+/// Result is cached for the duration of the test run.
+async fn jira_credentials_valid() -> bool {
+ if !jira_configured() {
+ return false;
+ }
+
+ *JIRA_CREDENTIALS_VALID
+ .get_or_init(|| async {
+ match JiraProvider::from_env() {
+ Ok(provider) => match provider.test_connection().await {
+ Ok(valid) => {
+ if !valid {
+ eprintln!(
+ "Jira credentials validation failed: connection test returned false"
+ );
+ }
+ valid
+ }
+ Err(e) => {
+ eprintln!("Jira credentials validation failed: {}", e);
+ false
+ }
+ },
+ Err(e) => {
+ eprintln!("Jira provider initialization failed: {}", e);
+ false
+ }
+ }
+ })
+ .await
+}
+
+/// Validate Linear credentials by testing the connection.
+/// Result is cached for the duration of the test run.
+async fn linear_credentials_valid() -> bool {
+ if !linear_configured() {
+ return false;
+ }
+
+ *LINEAR_CREDENTIALS_VALID
+ .get_or_init(|| async {
+ match LinearProvider::from_env() {
+ Ok(provider) => match provider.test_connection().await {
+ Ok(valid) => {
+ if !valid {
+ eprintln!(
+ "Linear credentials validation failed: connection test returned false"
+ );
+ }
+ valid
+ }
+ Err(e) => {
+ eprintln!("Linear credentials validation failed: {}", e);
+ false
+ }
+ },
+ Err(e) => {
+ eprintln!("Linear provider initialization failed: {}", e);
+ false
+ }
+ }
+ })
+ .await
+}
+
+/// Macro to skip test if provider is not configured or credentials are invalid
macro_rules! skip_if_not_configured {
- ($configured:expr, $provider:expr) => {
+ ($configured:expr, $valid:expr, $provider:expr) => {
if !$configured {
eprintln!("Skipping test: {} credentials not configured", $provider);
return;
}
+ if !$valid.await {
+ eprintln!(
+ "Skipping test: {} credentials invalid or expired",
+ $provider
+ );
+ return;
+ }
};
}
@@ -100,7 +191,7 @@ mod jira_tests {
#[tokio::test]
async fn test_connection() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let result = provider.test_connection().await;
@@ -110,7 +201,7 @@ mod jira_tests {
#[tokio::test]
async fn test_list_projects() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let projects = provider
@@ -131,7 +222,7 @@ mod jira_tests {
#[tokio::test]
async fn test_list_users() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let project = jira_test_project();
@@ -150,7 +241,7 @@ mod jira_tests {
#[tokio::test]
async fn test_list_statuses() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let project = jira_test_project();
@@ -165,7 +256,7 @@ mod jira_tests {
#[tokio::test]
async fn test_get_issue_types() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let project = jira_test_project();
@@ -183,7 +274,7 @@ mod jira_tests {
#[tokio::test]
async fn test_list_issues() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let project = jira_test_project();
@@ -217,7 +308,7 @@ mod jira_tests {
#[tokio::test]
async fn test_create_issue() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let project = jira_test_project();
@@ -248,7 +339,7 @@ mod jira_tests {
#[tokio::test]
async fn test_update_issue_status() {
- skip_if_not_configured!(jira_configured(), "Jira");
+ skip_if_not_configured!(jira_configured(), jira_credentials_valid(), "Jira");
let provider = get_provider();
let project = jira_test_project();
@@ -320,7 +411,7 @@ mod linear_tests {
#[tokio::test]
async fn test_connection() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let result = provider.test_connection().await;
@@ -330,7 +421,7 @@ mod linear_tests {
#[tokio::test]
async fn test_list_projects() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let teams = provider.list_projects().await.expect("Should list teams");
@@ -348,7 +439,7 @@ mod linear_tests {
#[tokio::test]
async fn test_list_users() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let team = linear_test_team();
@@ -368,7 +459,7 @@ mod linear_tests {
#[tokio::test]
async fn test_list_statuses() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let team = linear_test_team();
@@ -383,7 +474,7 @@ mod linear_tests {
#[tokio::test]
async fn test_get_issue_types() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let team = linear_test_team();
@@ -401,7 +492,7 @@ mod linear_tests {
#[tokio::test]
async fn test_list_issues() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let team = linear_test_team();
@@ -427,7 +518,7 @@ mod linear_tests {
#[tokio::test]
async fn test_create_issue() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let team = linear_test_team();
@@ -477,7 +568,7 @@ mod linear_tests {
#[tokio::test]
async fn test_update_issue_status() {
- skip_if_not_configured!(linear_configured(), "Linear");
+ skip_if_not_configured!(linear_configured(), linear_credentials_valid(), "Linear");
let provider = get_provider();
let team = linear_test_team();
@@ -598,11 +689,11 @@ mod linear_tests {
#[tokio::test]
async fn test_provider_interface_consistency() {
// This test verifies both providers implement the same interface
- let jira_ok = jira_configured();
- let linear_ok = linear_configured();
+ let jira_ok = jira_configured() && jira_credentials_valid().await;
+ let linear_ok = linear_configured() && linear_credentials_valid().await;
if !jira_ok && !linear_ok {
- eprintln!("Skipping: No providers configured");
+ eprintln!("Skipping: No providers configured or credentials invalid");
return;
}
diff --git a/vscode-extension/package-lock.json b/vscode-extension/package-lock.json
index 9952850..865f5f0 100644
--- a/vscode-extension/package-lock.json
+++ b/vscode-extension/package-lock.json
@@ -24,6 +24,7 @@
"eslint": "^8.56.0",
"glob": "^10.3.10",
"mocha": "^10.2.0",
+ "nyc": "^17.1.0",
"sinon": "^17.0.1",
"source-map-support": "^0.5.21",
"typescript": "^5.3.3"
@@ -207,7 +208,6 @@
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0",
@@ -223,7 +223,6 @@
"integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6.9.0"
}
@@ -234,7 +233,6 @@
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -265,8 +263,7 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
@@ -274,7 +271,6 @@
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
- "peer": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -285,7 +281,6 @@
"integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/parser": "^7.28.5",
"@babel/types": "^7.28.5",
@@ -303,7 +298,6 @@
"integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/compat-data": "^7.27.2",
"@babel/helper-validator-option": "^7.27.1",
@@ -321,7 +315,6 @@
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"yallist": "^3.0.2"
}
@@ -332,7 +325,6 @@
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
- "peer": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -342,8 +334,7 @@
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/@babel/helper-globals": {
"version": "7.28.0",
@@ -351,7 +342,6 @@
"integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6.9.0"
}
@@ -362,7 +352,6 @@
"integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/traverse": "^7.27.1",
"@babel/types": "^7.27.1"
@@ -377,7 +366,6 @@
"integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/helper-module-imports": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1",
@@ -396,7 +384,6 @@
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6.9.0"
}
@@ -407,7 +394,6 @@
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6.9.0"
}
@@ -418,7 +404,6 @@
"integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6.9.0"
}
@@ -429,7 +414,6 @@
"integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/template": "^7.27.2",
"@babel/types": "^7.28.4"
@@ -444,7 +428,6 @@
"integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/types": "^7.28.5"
},
@@ -461,7 +444,6 @@
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/parser": "^7.27.2",
@@ -477,7 +459,6 @@
"integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.27.1",
"@babel/generator": "^7.28.5",
@@ -497,7 +478,6 @@
"integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.28.5"
@@ -718,7 +698,6 @@
"integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"camelcase": "^5.3.1",
"find-up": "^4.1.0",
@@ -736,7 +715,6 @@
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"sprintf-js": "~1.0.2"
}
@@ -747,7 +725,6 @@
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6"
}
@@ -758,7 +735,6 @@
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -773,7 +749,6 @@
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
@@ -788,7 +763,6 @@
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -802,7 +776,6 @@
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-try": "^2.0.0"
},
@@ -819,7 +792,6 @@
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -833,7 +805,6 @@
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -870,7 +841,6 @@
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
@@ -882,7 +852,6 @@
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
@@ -1621,7 +1590,6 @@
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"clean-stack": "^2.0.0",
"indent-string": "^4.0.0"
@@ -1700,7 +1668,6 @@
"integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"default-require-extensions": "^3.0.0"
},
@@ -1713,8 +1680,7 @@
"resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
"integrity": "sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/argparse": {
"version": "2.0.1",
@@ -1786,7 +1752,6 @@
"integrity": "sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==",
"dev": true,
"license": "Apache-2.0",
- "peer": true,
"bin": {
"baseline-browser-mapping": "dist/cli.js"
}
@@ -1890,7 +1855,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -2021,7 +1985,6 @@
"integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"hasha": "^5.0.0",
"make-dir": "^3.0.0",
@@ -2038,7 +2001,6 @@
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"semver": "^6.0.0"
},
@@ -2055,7 +2017,6 @@
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
- "peer": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -2133,8 +2094,7 @@
"url": "https://github.com/sponsors/ai"
}
],
- "license": "CC-BY-4.0",
- "peer": true
+ "license": "CC-BY-4.0"
},
"node_modules/chalk": {
"version": "2.4.2",
@@ -2247,7 +2207,6 @@
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6"
}
@@ -2427,8 +2386,7 @@
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
@@ -2442,8 +2400,7 @@
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/core-util-is": {
"version": "1.0.3",
@@ -2599,7 +2556,6 @@
"integrity": "sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"strip-bom": "^4.0.0"
},
@@ -2776,8 +2732,7 @@
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
"integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/emoji-regex": {
"version": "9.2.2",
@@ -2878,8 +2833,7 @@
"resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
"integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/escalade": {
"version": "3.2.0",
@@ -3138,7 +3092,6 @@
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true,
"license": "BSD-2-Clause",
- "peer": true,
"bin": {
"esparse": "bin/esparse.js",
"esvalidate": "bin/esvalidate.js"
@@ -3294,7 +3247,6 @@
"integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"commondir": "^1.0.1",
"make-dir": "^3.0.2",
@@ -3313,7 +3265,6 @@
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"semver": "^6.0.0"
},
@@ -3330,7 +3281,6 @@
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
- "peer": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -3437,8 +3387,7 @@
"url": "https://feross.org/support"
}
],
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/fs-constants": {
"version": "1.0.0",
@@ -3486,7 +3435,6 @@
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6.9.0"
}
@@ -3545,7 +3493,6 @@
"integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -3677,8 +3624,7 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/graphemer": {
"version": "1.4.0",
@@ -3732,7 +3678,6 @@
"integrity": "sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"is-stream": "^2.0.0",
"type-fest": "^0.8.0"
@@ -3750,7 +3695,6 @@
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true,
"license": "(MIT OR CC0-1.0)",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -3944,7 +3888,6 @@
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -4106,7 +4049,6 @@
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
},
@@ -4119,8 +4061,7 @@
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/is-unicode-supported": {
"version": "0.1.0",
@@ -4141,7 +4082,6 @@
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -4192,7 +4132,6 @@
"integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==",
"dev": true,
"license": "BSD-3-Clause",
- "peer": true,
"dependencies": {
"append-transform": "^2.0.0"
},
@@ -4206,7 +4145,6 @@
"integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
"dev": true,
"license": "BSD-3-Clause",
- "peer": true,
"dependencies": {
"@babel/core": "^7.23.9",
"@babel/parser": "^7.23.9",
@@ -4224,7 +4162,6 @@
"integrity": "sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"archy": "^1.0.0",
"cross-spawn": "^7.0.3",
@@ -4281,7 +4218,6 @@
"integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
"dev": true,
"license": "BSD-3-Clause",
- "peer": true,
"dependencies": {
"debug": "^4.1.1",
"istanbul-lib-coverage": "^3.0.0",
@@ -4326,8 +4262,7 @@
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.1",
@@ -4348,7 +4283,6 @@
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"jsesc": "bin/jsesc"
},
@@ -4383,7 +4317,6 @@
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"json5": "lib/cli.js"
},
@@ -4552,8 +4485,7 @@
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
"integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/lodash.includes": {
"version": "4.3.0",
@@ -5196,7 +5128,6 @@
"integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"process-on-spawn": "^1.0.0"
},
@@ -5209,8 +5140,7 @@
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/normalize-path": {
"version": "3.0.0",
@@ -5241,7 +5171,6 @@
"integrity": "sha512-U42vQ4czpKa0QdI1hu950XuNhYqgoM+ZF1HT+VuUHL9hPfDPVvNQyltmMqdE9bUHMVa+8yNbc3QKTj8zQhlVxQ==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"@istanbuljs/load-nyc-config": "^1.0.0",
"@istanbuljs/schema": "^0.1.2",
@@ -5284,7 +5213,6 @@
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -5301,7 +5229,6 @@
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -5313,7 +5240,6 @@
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6"
}
@@ -5324,7 +5250,6 @@
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@@ -5337,7 +5262,6 @@
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -5350,8 +5274,7 @@
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/nyc/node_modules/decamelize": {
"version": "1.2.0",
@@ -5359,7 +5282,6 @@
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5369,8 +5291,7 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/nyc/node_modules/find-up": {
"version": "4.1.0",
@@ -5378,7 +5299,6 @@
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -5394,7 +5314,6 @@
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@@ -5416,7 +5335,6 @@
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -5430,7 +5348,6 @@
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"semver": "^6.0.0"
},
@@ -5447,7 +5364,6 @@
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -5461,7 +5377,6 @@
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-try": "^2.0.0"
},
@@ -5478,7 +5393,6 @@
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -5492,7 +5406,6 @@
"integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -5503,7 +5416,6 @@
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
- "peer": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -5513,8 +5425,7 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/nyc/node_modules/string-width": {
"version": "4.2.3",
@@ -5522,7 +5433,6 @@
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
@@ -5538,7 +5448,6 @@
"integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"@istanbuljs/schema": "^0.1.2",
"glob": "^7.1.4",
@@ -5554,7 +5463,6 @@
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -5569,8 +5477,7 @@
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/nyc/node_modules/yargs": {
"version": "15.4.1",
@@ -5578,7 +5485,6 @@
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
@@ -5602,7 +5508,6 @@
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
@@ -5859,7 +5764,6 @@
"integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"aggregate-error": "^3.0.0"
},
@@ -5873,7 +5777,6 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=6"
}
@@ -5884,7 +5787,6 @@
"integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"graceful-fs": "^4.1.15",
"hasha": "^5.0.0",
@@ -6078,8 +5980,7 @@
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -6100,7 +6001,6 @@
"integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"find-up": "^4.0.0"
},
@@ -6114,7 +6014,6 @@
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@@ -6129,7 +6028,6 @@
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-locate": "^4.1.0"
},
@@ -6143,7 +6041,6 @@
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-try": "^2.0.0"
},
@@ -6160,7 +6057,6 @@
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"p-limit": "^2.2.0"
},
@@ -6219,7 +6115,6 @@
"integrity": "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"fromentries": "^1.2.0"
},
@@ -6379,7 +6274,6 @@
"integrity": "sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"es6-error": "^4.0.1"
},
@@ -6402,8 +6296,7 @@
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/resolve-from": {
"version": "4.0.0",
@@ -6609,8 +6502,7 @@
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/setimmediate": {
"version": "1.0.5",
@@ -6859,7 +6751,6 @@
"integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"foreground-child": "^2.0.0",
"is-windows": "^1.0.2",
@@ -6878,7 +6769,6 @@
"integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"cross-spawn": "^7.0.0",
"signal-exit": "^3.0.2"
@@ -6893,7 +6783,6 @@
"integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"semver": "^6.0.0"
},
@@ -6910,7 +6799,6 @@
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"dev": true,
"license": "ISC",
- "peer": true,
"bin": {
"semver": "bin/semver.js"
}
@@ -6920,16 +6808,14 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
"dev": true,
- "license": "BSD-3-Clause",
- "peer": true
+ "license": "BSD-3-Clause"
},
"node_modules/stdin-discarder": {
"version": "0.2.2",
@@ -7064,7 +6950,6 @@
"integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=8"
}
@@ -7302,7 +7187,6 @@
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"is-typedarray": "^1.0.0"
}
@@ -7372,7 +7256,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
@@ -7485,8 +7368,7 @@
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/word-wrap": {
"version": "1.2.5",
@@ -7655,7 +7537,6 @@
"integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
"dev": true,
"license": "ISC",
- "peer": true,
"dependencies": {
"imurmurhash": "^0.1.4",
"is-typedarray": "^1.0.0",
@@ -7668,8 +7549,7 @@
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true,
- "license": "ISC",
- "peer": true
+ "license": "ISC"
},
"node_modules/wsl-utils": {
"version": "0.1.0",
diff --git a/vscode-extension/package.json b/vscode-extension/package.json
index 5646e0e..bc7488c 100644
--- a/vscode-extension/package.json
+++ b/vscode-extension/package.json
@@ -229,13 +229,13 @@
"pretest": "npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "vscode-test",
- "test:coverage": "c8 npm run test",
- "coverage:report": "c8 report --reporter=lcov",
+ "test:coverage": "npm run test",
"package": "vsce package",
"publish": "vsce publish"
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2",
+ "nyc": "^17.1.0",
"@types/glob": "^8.1.0",
"@types/mocha": "^10.0.6",
"@types/node": "20.x",
diff --git a/vscode-extension/src/operator-binary.ts b/vscode-extension/src/operator-binary.ts
index 1539cbd..1e8f685 100644
--- a/vscode-extension/src/operator-binary.ts
+++ b/vscode-extension/src/operator-binary.ts
@@ -28,11 +28,11 @@ export function getExtensionVersion(): string {
*
* Supported platforms:
* - darwin + arm64 -> operator-macos-arm64
- * - darwin + x64 -> operator-macos-x86_64
* - linux + arm64 -> operator-linux-arm64
* - linux + x64 -> operator-linux-x86_64
* - win32 + x64 -> operator-windows-x86_64.exe
- * - win32 + arm64 -> operator-windows-arm64.exe
+ *
+ * Unsupported platforms fall back to system PATH lookup
*/
function getArtifactName(): string {
const platform = process.platform; // 'darwin', 'linux', 'win32'
diff --git a/vscode-extension/test/suite/index.ts b/vscode-extension/test/suite/index.ts
index 73bdda0..192a81a 100644
--- a/vscode-extension/test/suite/index.ts
+++ b/vscode-extension/test/suite/index.ts
@@ -2,15 +2,46 @@ import * as path from 'path';
import Mocha from 'mocha';
import { glob } from 'glob';
+// NYC for coverage instrumentation inside VS Code process
+// eslint-disable-next-line @typescript-eslint/no-require-imports
+const NYC = require('nyc');
+
export async function run(): Promise {
+ const testsRoot = path.resolve(__dirname, '.');
+ const workspaceRoot = path.join(__dirname, '..', '..', '..');
+
+ // Setup NYC for coverage inside VS Code process
+ const nyc = new NYC({
+ cwd: workspaceRoot,
+ reporter: ['text', 'lcov', 'html'],
+ all: true,
+ silent: false,
+ instrument: true,
+ hookRequire: true,
+ hookRunInContext: true,
+ hookRunInThisContext: true,
+ include: ['out/src/**/*.js'],
+ exclude: ['out/test/**', 'out/src/generated/**'],
+ reportDir: path.join(workspaceRoot, 'coverage'),
+ });
+
+ await nyc.reset();
+ await nyc.wrap();
+
+ // Re-require already-loaded modules for instrumentation
+ Object.keys(require.cache)
+ .filter((f) => nyc.exclude.shouldInstrument(f))
+ .forEach((m) => {
+ delete require.cache[m];
+ require(m);
+ });
+
// Create the mocha test
const mocha = new Mocha({
ui: 'tdd',
color: true,
});
- const testsRoot = path.resolve(__dirname, '.');
-
const files = await glob('**/**.test.js', { cwd: testsRoot });
// Add files to the test suite
@@ -18,7 +49,14 @@ export async function run(): Promise {
// Run the mocha test
return new Promise((resolve, reject) => {
- mocha.run((failures) => {
+ mocha.run(async (failures) => {
+ // Write coverage data
+ await nyc.writeCoverageFile();
+
+ // Generate and display coverage report
+ console.log('\n--- Coverage Report ---');
+ await captureStdout(nyc.report.bind(nyc));
+
if (failures > 0) {
reject(new Error(`${failures} tests failed.`));
} else {
@@ -27,3 +65,16 @@ export async function run(): Promise {
});
});
}
+
+async function captureStdout(fn: () => Promise): Promise {
+ const originalWrite = process.stdout.write.bind(process.stdout);
+ let buffer = '';
+ process.stdout.write = (s: string): boolean => {
+ buffer += s;
+ originalWrite(s);
+ return true;
+ };
+ await fn();
+ process.stdout.write = originalWrite;
+ return buffer;
+}
diff --git a/vscode-extension/test/suite/operator-binary.test.ts b/vscode-extension/test/suite/operator-binary.test.ts
new file mode 100644
index 0000000..a7e5942
--- /dev/null
+++ b/vscode-extension/test/suite/operator-binary.test.ts
@@ -0,0 +1,424 @@
+/**
+ * Tests for operator-binary.ts
+ *
+ * Tests the operator binary discovery, download URL generation,
+ * version checking, and path resolution functions.
+ */
+
+import * as assert from 'assert';
+import * as sinon from 'sinon';
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs/promises';
+import * as os from 'os';
+import {
+ getExtensionVersion,
+ getDownloadUrl,
+ getStoragePath,
+ getOperatorPath,
+ isOperatorAvailable,
+ getOperatorVersion,
+} from '../../src/operator-binary';
+
+suite('Operator Binary Test Suite', () => {
+ let sandbox: sinon.SinonSandbox;
+
+ // Platform-specific binary name
+ const binaryName = process.platform === 'win32' ? 'operator.exe' : 'operator';
+
+ setup(() => {
+ sandbox = sinon.createSandbox();
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+ suite('getExtensionVersion()', () => {
+ test('returns version from extension packageJSON', () => {
+ sandbox.stub(vscode.extensions, 'getExtension').returns({
+ packageJSON: { version: '1.2.3' },
+ } as vscode.Extension);
+
+ const version = getExtensionVersion();
+ assert.strictEqual(version, '1.2.3');
+ });
+
+ test('falls back to 0.2.0 when extension not found', () => {
+ sandbox.stub(vscode.extensions, 'getExtension').returns(undefined);
+
+ const version = getExtensionVersion();
+ assert.strictEqual(version, '0.2.0');
+ });
+
+ test('falls back to 0.2.0 when packageJSON has no version', () => {
+ sandbox.stub(vscode.extensions, 'getExtension').returns({
+ packageJSON: {},
+ } as vscode.Extension);
+
+ const version = getExtensionVersion();
+ assert.strictEqual(version, '0.2.0');
+ });
+ });
+
+ suite('getDownloadUrl()', () => {
+ test('generates correct URL format with explicit version', () => {
+ const url = getDownloadUrl('1.0.0');
+
+ assert.ok(url.startsWith('https://github.com/untra/operator/releases/download/v1.0.0/'));
+ assert.ok(url.includes('operator-'));
+ });
+
+ test('uses extension version when none provided', () => {
+ sandbox.stub(vscode.extensions, 'getExtension').returns({
+ packageJSON: { version: '2.3.4' },
+ } as vscode.Extension);
+
+ const url = getDownloadUrl();
+
+ assert.ok(url.includes('/v2.3.4/'));
+ });
+
+ test('includes platform-specific binary name', () => {
+ const url = getDownloadUrl('1.0.0');
+
+ // Check that it includes platform-specific naming
+ if (process.platform === 'darwin') {
+ assert.ok(url.includes('operator-macos-'), `Expected macos in URL: ${url}`);
+ } else if (process.platform === 'linux') {
+ assert.ok(url.includes('operator-linux-'), `Expected linux in URL: ${url}`);
+ } else if (process.platform === 'win32') {
+ assert.ok(url.includes('operator-windows-'), `Expected windows in URL: ${url}`);
+ assert.ok(url.endsWith('.exe'), 'Windows URL should end with .exe');
+ }
+ });
+
+ test('includes architecture-specific binary name', () => {
+ const url = getDownloadUrl('1.0.0');
+
+ // Check architecture naming
+ if (process.arch === 'arm64') {
+ assert.ok(url.includes('-arm64'), `Expected arm64 in URL: ${url}`);
+ } else if (process.arch === 'x64') {
+ assert.ok(url.includes('-x86_64'), `Expected x86_64 in URL: ${url}`);
+ }
+ });
+ });
+
+ suite('getStoragePath()', () => {
+ test('returns correct path for Unix platforms', () => {
+ // Skip on Windows
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ const mockContext = {
+ globalStorageUri: { fsPath: '/home/user/.vscode/extensions/storage' },
+ } as unknown as vscode.ExtensionContext;
+
+ const storagePath = getStoragePath(mockContext);
+
+ assert.strictEqual(storagePath, '/home/user/.vscode/extensions/storage/operator');
+ });
+
+ test('returns correct path with .exe for Windows', () => {
+ // We test the logic by checking that Windows would get .exe
+ const mockContext = {
+ globalStorageUri: { fsPath: 'C:\\Users\\user\\.vscode\\storage' },
+ } as unknown as vscode.ExtensionContext;
+
+ const storagePath = getStoragePath(mockContext);
+
+ if (process.platform === 'win32') {
+ assert.ok(storagePath.endsWith('operator.exe'));
+ } else {
+ assert.ok(storagePath.endsWith('operator'));
+ assert.ok(!storagePath.endsWith('.exe'));
+ }
+ });
+ });
+
+ suite('getOperatorPath()', () => {
+ let tempDir: string;
+
+ setup(async () => {
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'operator-binary-test-'));
+ });
+
+ teardown(async () => {
+ try {
+ await fs.rm(tempDir, { recursive: true });
+ } catch {
+ // Ignore cleanup errors
+ }
+ });
+
+ test('returns configured path when set and file exists', async () => {
+ // Create a mock operator binary
+ const operatorPath = path.join(tempDir, 'my-operator');
+ await fs.writeFile(operatorPath, '#!/bin/bash\necho "operator"');
+ await fs.chmod(operatorPath, 0o755);
+
+ // Mock config to return the path
+ const configStub = sandbox.stub(vscode.workspace, 'getConfiguration');
+ configStub.returns({
+ get: (key: string) => {
+ if (key === 'operatorPath') {
+ return operatorPath;
+ }
+ return undefined;
+ },
+ } as unknown as vscode.WorkspaceConfiguration);
+
+ const mockContext = {
+ globalStorageUri: { fsPath: path.join(tempDir, 'storage') },
+ } as unknown as vscode.ExtensionContext;
+
+ const result = await getOperatorPath(mockContext);
+ assert.strictEqual(result, operatorPath);
+ });
+
+ test('returns storage path when config empty but storage binary exists', async () => {
+ // Create storage directory and binary
+ const storagePath = path.join(tempDir, 'storage');
+ await fs.mkdir(storagePath, { recursive: true });
+ const binaryPath = path.join(storagePath, binaryName);
+ await fs.writeFile(binaryPath, '#!/bin/bash\necho "operator"');
+ await fs.chmod(binaryPath, 0o755);
+
+ // Mock config to return empty
+ const configStub = sandbox.stub(vscode.workspace, 'getConfiguration');
+ configStub.returns({
+ get: () => '',
+ } as unknown as vscode.WorkspaceConfiguration);
+
+ const mockContext = {
+ globalStorageUri: { fsPath: storagePath },
+ } as unknown as vscode.ExtensionContext;
+
+ const result = await getOperatorPath(mockContext);
+ assert.strictEqual(result, binaryPath);
+ });
+
+ test('looks up in PATH for non-existent storage (integration)', async () => {
+ // This test actually invokes the PATH lookup
+ // It tests that when config and storage are empty, we call which/where
+
+ // Mock config to return empty
+ const configStub = sandbox.stub(vscode.workspace, 'getConfiguration');
+ configStub.returns({
+ get: () => '',
+ } as unknown as vscode.WorkspaceConfiguration);
+
+ const mockContext = {
+ globalStorageUri: { fsPath: path.join(tempDir, 'nonexistent-storage') },
+ } as unknown as vscode.ExtensionContext;
+
+ // This will actually call which/where - operator may or may not be in PATH
+ // We're testing that the function completes without error
+ const result = await getOperatorPath(mockContext);
+
+ // Result could be a path or undefined - we just verify the function works
+ assert.ok(result === undefined || typeof result === 'string');
+ });
+
+ test('ignores configured path when file does not exist', async () => {
+ // Mock config to return a non-existent path
+ const configStub = sandbox.stub(vscode.workspace, 'getConfiguration');
+ configStub.returns({
+ get: (key: string) => {
+ if (key === 'operatorPath') {
+ return '/nonexistent/path/operator';
+ }
+ return undefined;
+ },
+ } as unknown as vscode.WorkspaceConfiguration);
+
+ const mockContext = {
+ globalStorageUri: { fsPath: path.join(tempDir, 'nonexistent-storage') },
+ } as unknown as vscode.ExtensionContext;
+
+ // Will fall through to PATH lookup since config path doesn't exist
+ const result = await getOperatorPath(mockContext);
+
+ // Should not return the non-existent config path
+ assert.notStrictEqual(result, '/nonexistent/path/operator');
+ });
+ });
+
+ suite('getOperatorVersion()', () => {
+ let tempDir: string;
+
+ setup(async () => {
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'operator-version-test-'));
+ });
+
+ teardown(async () => {
+ try {
+ await fs.rm(tempDir, { recursive: true });
+ } catch {
+ // Ignore cleanup errors
+ }
+ });
+
+ test('parses version from "operator X.Y.Z" format', async () => {
+ // Skip on Windows - shell scripts don't work the same way
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ // Create a mock operator binary that outputs version
+ const operatorPath = path.join(tempDir, 'operator');
+ await fs.writeFile(operatorPath, '#!/bin/bash\necho "operator 0.1.14"');
+ await fs.chmod(operatorPath, 0o755);
+
+ const version = await getOperatorVersion(operatorPath);
+ assert.strictEqual(version, '0.1.14');
+ });
+
+ test('returns trimmed output when no match pattern', async () => {
+ // Skip on Windows
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ const operatorPath = path.join(tempDir, 'operator');
+ await fs.writeFile(operatorPath, '#!/bin/bash\necho "1.2.3"');
+ await fs.chmod(operatorPath, 0o755);
+
+ const version = await getOperatorVersion(operatorPath);
+ assert.strictEqual(version, '1.2.3');
+ });
+
+ test('returns undefined on non-zero exit code', async () => {
+ // Skip on Windows
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ const operatorPath = path.join(tempDir, 'operator');
+ await fs.writeFile(operatorPath, '#!/bin/bash\nexit 1');
+ await fs.chmod(operatorPath, 0o755);
+
+ const version = await getOperatorVersion(operatorPath);
+ assert.strictEqual(version, undefined);
+ });
+
+ test('returns undefined for non-existent binary', async () => {
+ const version = await getOperatorVersion('/nonexistent/path/operator');
+ assert.strictEqual(version, undefined);
+ });
+
+ test('returns undefined on empty output', async () => {
+ // Skip on Windows
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ const operatorPath = path.join(tempDir, 'operator');
+ await fs.writeFile(operatorPath, '#!/bin/bash\necho ""');
+ await fs.chmod(operatorPath, 0o755);
+
+ const version = await getOperatorVersion(operatorPath);
+ // Empty output after trim is falsy, so returns undefined
+ assert.strictEqual(version, undefined);
+ });
+
+ test('handles version with additional text', async () => {
+ // Skip on Windows
+ if (process.platform === 'win32') {
+ return;
+ }
+
+ const operatorPath = path.join(tempDir, 'operator');
+ await fs.writeFile(operatorPath, '#!/bin/bash\necho "operator 2.0.0-beta.1"');
+ await fs.chmod(operatorPath, 0o755);
+
+ const version = await getOperatorVersion(operatorPath);
+ assert.strictEqual(version, '2.0.0-beta.1');
+ });
+ });
+
+ suite('isOperatorAvailable()', () => {
+ let tempDir: string;
+
+ setup(async () => {
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'operator-avail-test-'));
+ });
+
+ teardown(async () => {
+ try {
+ await fs.rm(tempDir, { recursive: true });
+ } catch {
+ // Ignore cleanup errors
+ }
+ });
+
+ test('returns true when operator is found in storage', async () => {
+ // Create storage directory and binary
+ const storagePath = path.join(tempDir, 'storage');
+ await fs.mkdir(storagePath, { recursive: true });
+ const binaryPath = path.join(storagePath, binaryName);
+ await fs.writeFile(binaryPath, '#!/bin/bash\necho "operator"');
+ await fs.chmod(binaryPath, 0o755);
+
+ // Mock config to return empty
+ const configStub = sandbox.stub(vscode.workspace, 'getConfiguration');
+ configStub.returns({
+ get: () => '',
+ } as unknown as vscode.WorkspaceConfiguration);
+
+ const mockContext = {
+ globalStorageUri: { fsPath: storagePath },
+ } as unknown as vscode.ExtensionContext;
+
+ const result = await isOperatorAvailable(mockContext);
+ assert.strictEqual(result, true);
+ });
+
+ test('returns true when operator is in configured path', async () => {
+ // Create a mock operator binary
+ const operatorPath = path.join(tempDir, 'my-operator');
+ await fs.writeFile(operatorPath, '#!/bin/bash\necho "operator"');
+ await fs.chmod(operatorPath, 0o755);
+
+ // Mock config to return the path
+ const configStub = sandbox.stub(vscode.workspace, 'getConfiguration');
+ configStub.returns({
+ get: (key: string) => {
+ if (key === 'operatorPath') {
+ return operatorPath;
+ }
+ return undefined;
+ },
+ } as unknown as vscode.WorkspaceConfiguration);
+
+ const mockContext = {
+ globalStorageUri: { fsPath: path.join(tempDir, 'storage') },
+ } as unknown as vscode.ExtensionContext;
+
+ const result = await isOperatorAvailable(mockContext);
+ assert.strictEqual(result, true);
+ });
+
+ test('returns false when operator is not found anywhere', async () => {
+ // Mock config to return empty
+ const configStub = sandbox.stub(vscode.workspace, 'getConfiguration');
+ configStub.returns({
+ get: () => '',
+ } as unknown as vscode.WorkspaceConfiguration);
+
+ // Use a unique temp storage path where operator won't exist
+ const mockContext = {
+ globalStorageUri: { fsPath: path.join(tempDir, 'empty-storage-' + Date.now()) },
+ } as unknown as vscode.ExtensionContext;
+
+ const result = await isOperatorAvailable(mockContext);
+
+ // If operator is not in PATH either, this should be false
+ // Note: if operator IS in PATH on the test machine, this could be true
+ // We're mainly testing that the function executes without error
+ assert.ok(typeof result === 'boolean');
+ });
+ });
+});