From 5f04c8f143a86d5eff677673ef9627d6cada1a6a Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:40:26 +1100 Subject: [PATCH 01/20] create dev container --- .devcontainer/Dockerfile | 68 +++++++++++++++++++++++++++ .devcontainer/devcontainer.json | 41 +++++++++++++++++ .devcontainer/post-create.sh | 81 +++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/post-create.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..202c8d1 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,68 @@ +# Dev container for dart_node monorepo +# Dart + Node.js + all build/test tooling +FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 + +ARG DART_VERSION=3.10.0 +ARG NODE_MAJOR=20 + +# System dependencies +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get install -y --no-install-recommends \ + apt-transport-https \ + bc \ + build-essential \ + ca-certificates \ + curl \ + git \ + gnupg \ + jq \ + lcov \ + lsof \ + python3 \ + unzip \ + wget \ + xvfb \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Dart SDK +RUN set -eux; \ + case "$(dpkg --print-architecture)" in \ + amd64) DART_ARCH="x64" ;; \ + arm64) DART_ARCH="arm64" ;; \ + *) echo "Unsupported arch"; exit 1 ;; \ + esac; \ + curl -fsSL "https://storage.googleapis.com/dart-archive/channels/stable/release/${DART_VERSION}/sdk/dartsdk-linux-${DART_ARCH}-release.zip" -o /tmp/dart-sdk.zip \ + && unzip /tmp/dart-sdk.zip -d /usr/lib \ + && rm /tmp/dart-sdk.zip + +ENV PATH="/usr/lib/dart-sdk/bin:${PATH}" +ENV PATH="/home/vscode/.pub-cache/bin:${PATH}" + +# Node.js via NodeSource +RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \ + && apt-get install -y nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Chromium for browser tests +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get install -y --no-install-recommends chromium-browser \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + || true + +# Global npm tools +RUN npm install -g cspell + +# Global Dart tools (as vscode user so pub-cache is in the right place) +USER vscode +RUN dart pub global activate coverage +USER root + +# Playwright system deps (for website tests) +RUN npx playwright install-deps chromium 2>/dev/null || true + +# Chrome/Chromium env +ENV CHROME_EXECUTABLE="/usr/bin/chromium-browser" +ENV PUPPETEER_EXECUTABLE_PATH="/usr/bin/chromium-browser" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..827ace6 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,41 @@ +{ + "name": "dart_node", + "build": { + "dockerfile": "Dockerfile", + "args": { + "DART_VERSION": "3.10.0", + "NODE_MAJOR": "20" + } + }, + "features": { + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + "postCreateCommand": "bash .devcontainer/post-create.sh", + "remoteUser": "vscode", + "containerEnv": { + "MIN_COVERAGE": "80", + "CHROME_EXECUTABLE": "/usr/bin/chromium-browser", + "PUPPETEER_EXECUTABLE_PATH": "/usr/bin/chromium-browser" + }, + "customizations": { + "vscode": { + "extensions": [ + "Dart-Code.dart-code", + "streetsidesoftware.code-spell-checker" + ], + "settings": { + "dart.sdkPath": "/usr/lib/dart-sdk", + "editor.formatOnSave": true, + "dart.lineLength": 80 + } + } + }, + "mounts": [ + "source=dart_node_pub_cache,target=/home/vscode/.pub-cache,type=volume" + ], + "forwardPorts": [3000, 8080], + "portsAttributes": { + "3000": { "label": "Backend API (Express)", "onAutoForward": "notify" }, + "8080": { "label": "Frontend", "onAutoForward": "notify" } + } +} diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100755 index 0000000..a260506 --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Post-create: install ALL dependencies and pre-build the backend demo +# so ./run_dev.sh works immediately with zero setup +set -euo pipefail + +echo "===========================================" +echo " dart_node dev container setup" +echo "===========================================" +echo "" +echo "Dart: $(dart --version 2>&1)" +echo "Node: $(node --version)" +echo "npm: $(npm --version)" + +# ── 1. Build tool dependencies ────────────────────────────────────── +echo "" +echo "[1/6] Setting up build tool..." +(cd tools/build && dart pub get) + +# ── 2. Dart pub get for all packages ──────────────────────────────── +echo "" +echo "[2/6] Installing Dart dependencies (packages)..." +for dir in packages/*; do + [ -d "$dir" ] && [ -f "$dir/pubspec.yaml" ] && { + echo " $(basename "$dir")..." + (cd "$dir" && dart pub get) || echo " WARNING: pub get failed for $dir" + } +done + +# ── 3. Dart pub get for all examples ──────────────────────────────── +echo "" +echo "[3/6] Installing Dart dependencies (examples)..." +for dir in examples/*; do + [ -d "$dir" ] && [ -f "$dir/pubspec.yaml" ] && { + echo " $(basename "$dir")..." + (cd "$dir" && dart pub get) || echo " WARNING: pub get failed for $dir" + } +done + +# ── 4. npm install for all packages/examples with package.json ────── +echo "" +echo "[4/6] Installing npm dependencies..." +for dir in packages/* examples/*; do + [ -d "$dir" ] && [ -f "$dir/package.json" ] && { + echo " npm install: $(basename "$dir")..." + (cd "$dir" && npm install) || echo " WARNING: npm install failed for $dir" + } +done + +# Handle nested React Native dir +[ -d "examples/mobile/rn" ] && [ -f "examples/mobile/rn/package.json" ] && { + echo " npm install: mobile/rn..." + (cd examples/mobile/rn && npm install) || echo " WARNING: npm install failed for mobile/rn" +} + +# Website deps +[ -d "website" ] && [ -f "website/package.json" ] && { + echo " npm ci: website..." + (cd website && npm ci) || echo " WARNING: npm ci failed for website" +} + +# ── 5. Pre-build the backend demo ─────────────────────────────────── +echo "" +echo "[5/6] Pre-building backend demo..." +dart run tools/build/build.dart backend + +# ── 6. Playwright browsers (for website tests) ────────────────────── +echo "" +echo "[6/6] Installing Playwright browsers..." +(cd website && npx playwright install chromium) 2>/dev/null || echo " Playwright install skipped (optional)" + +# Ensure coverage tool is available +dart pub global activate coverage 2>/dev/null || true + +echo "" +echo "===========================================" +echo " Setup complete!" +echo "" +echo " Run the demo: ./run_dev.sh" +echo " Or manually: node examples/backend/build/server.js" +echo " API at: http://localhost:3000" +echo "===========================================" From e409083d1168fe4af21b516e05afc0da7b0ef3b2 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 20:26:57 +1100 Subject: [PATCH 02/20] Claude stuff --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 7e96a73..7f6add4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -35,6 +35,11 @@ Dart packages for building Node.js apps. Strongly Typed Dart layer over JS inter - All packages require: `austerity` (linting), `nadz` (Result types) - `node_preamble` for dart2js Node.js compatibility +**Pull Requests** +- Keep the documentation tight +- Use the template: .github/PULL_REQUEST_TEMPLATE.md +- Only use git diff with main. Ignore commit messages + # Web & Translation - Optimize for AI Search and SEO From 0019e7a5e49fb0843825537e5001c9e84f223d9b Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:14:34 +1100 Subject: [PATCH 03/20] Fix container --- .devcontainer/Dockerfile | 65 ++++++++++--------- .devcontainer/devcontainer.json | 14 ++-- .devcontainer/post-create.sh | 62 ++++++------------ README.md | 12 +++- README_zh.md | 2 +- examples/README.md | 4 +- examples/run_taskflow.sh | 75 ++++++++++++++++++++++ run_dev.sh | 109 -------------------------------- 8 files changed, 147 insertions(+), 196 deletions(-) create mode 100755 examples/run_taskflow.sh delete mode 100755 run_dev.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 202c8d1..24a4c2c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,31 +1,38 @@ # Dev container for dart_node monorepo -# Dart + Node.js + all build/test tooling +# Dart + Node.js — lean image, fast startup +# +# ┌─────────────────────────────────────────────────┐ +# │ PINNED VERSIONS — update here, nowhere else │ +# │ │ +# │ Dart SDK: 3.10.7 │ +# │ Node.js: 20.20.0 │ +# │ cspell: 9.6.0 │ +# │ coverage: 1.15.0 │ +# │ gh CLI: 2.67.0 (in devcontainer.json) │ +# └─────────────────────────────────────────────────┘ FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 -ARG DART_VERSION=3.10.0 -ARG NODE_MAJOR=20 +ARG DART_VERSION=3.10.7 +ARG NODE_VERSION=20.20.0 +ARG CSPELL_VERSION=9.6.0 +ARG COVERAGE_VERSION=1.15.0 -# System dependencies +# System dependencies (minimal) RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ && apt-get install -y --no-install-recommends \ - apt-transport-https \ bc \ build-essential \ ca-certificates \ curl \ git \ - gnupg \ jq \ - lcov \ lsof \ python3 \ unzip \ - wget \ - xvfb \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# Dart SDK +# Dart SDK (exact version) RUN set -eux; \ case "$(dpkg --print-architecture)" in \ amd64) DART_ARCH="x64" ;; \ @@ -39,30 +46,22 @@ RUN set -eux; \ ENV PATH="/usr/lib/dart-sdk/bin:${PATH}" ENV PATH="/home/vscode/.pub-cache/bin:${PATH}" -# Node.js via NodeSource -RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_MAJOR}.x | bash - \ - && apt-get install -y nodejs \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Chromium for browser tests -RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ - && apt-get install -y --no-install-recommends chromium-browser \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - || true +# Node.js (exact version, direct binary) +RUN set -eux; \ + case "$(dpkg --print-architecture)" in \ + amd64) NODE_ARCH="x64" ;; \ + arm64) NODE_ARCH="arm64" ;; \ + *) echo "Unsupported arch"; exit 1 ;; \ + esac; \ + curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-${NODE_ARCH}.tar.xz" -o /tmp/node.tar.xz \ + && tar -xJf /tmp/node.tar.xz -C /usr/local --strip-components=1 \ + && rm /tmp/node.tar.xz \ + && node --version && npm --version -# Global npm tools -RUN npm install -g cspell +# Global npm tools (pinned) +RUN npm install -g "cspell@${CSPELL_VERSION}" -# Global Dart tools (as vscode user so pub-cache is in the right place) +# Global Dart tools (pinned, as vscode user so pub-cache lands correctly) USER vscode -RUN dart pub global activate coverage +RUN dart pub global activate coverage "${COVERAGE_VERSION}" USER root - -# Playwright system deps (for website tests) -RUN npx playwright install-deps chromium 2>/dev/null || true - -# Chrome/Chromium env -ENV CHROME_EXECUTABLE="/usr/bin/chromium-browser" -ENV PUPPETEER_EXECUTABLE_PATH="/usr/bin/chromium-browser" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 827ace6..78f9653 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,19 +3,21 @@ "build": { "dockerfile": "Dockerfile", "args": { - "DART_VERSION": "3.10.0", - "NODE_MAJOR": "20" + "DART_VERSION": "3.10.7", + "NODE_VERSION": "20.20.0", + "CSPELL_VERSION": "9.6.0", + "COVERAGE_VERSION": "1.15.0" } }, "features": { - "ghcr.io/devcontainers/features/github-cli:1": {} + "ghcr.io/devcontainers/features/github-cli:1": { + "version": "2.67.0" + } }, "postCreateCommand": "bash .devcontainer/post-create.sh", "remoteUser": "vscode", "containerEnv": { - "MIN_COVERAGE": "80", - "CHROME_EXECUTABLE": "/usr/bin/chromium-browser", - "PUPPETEER_EXECUTABLE_PATH": "/usr/bin/chromium-browser" + "MIN_COVERAGE": "80" }, "customizations": { "vscode": { diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index a260506..0503c9a 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -1,6 +1,5 @@ #!/bin/bash -# Post-create: install ALL dependencies and pre-build the backend demo -# so ./run_dev.sh works immediately with zero setup +# Post-create: install deps and pre-build so run_taskflow.sh just works set -euo pipefail echo "===========================================" @@ -11,71 +10,46 @@ echo "Dart: $(dart --version 2>&1)" echo "Node: $(node --version)" echo "npm: $(npm --version)" -# ── 1. Build tool dependencies ────────────────────────────────────── +# ── 1. Build tool ──────────────────────────────────────────────────── echo "" -echo "[1/6] Setting up build tool..." +echo "[1/4] Setting up build tool..." (cd tools/build && dart pub get) -# ── 2. Dart pub get for all packages ──────────────────────────────── +# ── 2. Dart pub get ────────────────────────────────────────────────── echo "" -echo "[2/6] Installing Dart dependencies (packages)..." -for dir in packages/*; do - [ -d "$dir" ] && [ -f "$dir/pubspec.yaml" ] && { - echo " $(basename "$dir")..." - (cd "$dir" && dart pub get) || echo " WARNING: pub get failed for $dir" - } -done - -# ── 3. Dart pub get for all examples ──────────────────────────────── -echo "" -echo "[3/6] Installing Dart dependencies (examples)..." -for dir in examples/*; do +echo "[2/4] Installing Dart dependencies..." +for dir in packages/* examples/*; do [ -d "$dir" ] && [ -f "$dir/pubspec.yaml" ] && { echo " $(basename "$dir")..." (cd "$dir" && dart pub get) || echo " WARNING: pub get failed for $dir" } done -# ── 4. npm install for all packages/examples with package.json ────── +# ── 3. npm install ─────────────────────────────────────────────────── echo "" -echo "[4/6] Installing npm dependencies..." +echo "[3/4] Installing npm dependencies..." for dir in packages/* examples/*; do [ -d "$dir" ] && [ -f "$dir/package.json" ] && { - echo " npm install: $(basename "$dir")..." - (cd "$dir" && npm install) || echo " WARNING: npm install failed for $dir" + if [ -f "$dir/package-lock.json" ]; then + echo " npm ci: $(basename "$dir")..." + (cd "$dir" && npm ci) || echo " WARNING: npm ci failed for $dir" + else + echo " npm install: $(basename "$dir")..." + (cd "$dir" && npm install) || echo " WARNING: npm install failed for $dir" + fi } done -# Handle nested React Native dir -[ -d "examples/mobile/rn" ] && [ -f "examples/mobile/rn/package.json" ] && { - echo " npm install: mobile/rn..." - (cd examples/mobile/rn && npm install) || echo " WARNING: npm install failed for mobile/rn" -} - -# Website deps -[ -d "website" ] && [ -f "website/package.json" ] && { - echo " npm ci: website..." - (cd website && npm ci) || echo " WARNING: npm ci failed for website" -} - -# ── 5. Pre-build the backend demo ─────────────────────────────────── +# ── 4. Pre-build backend ──────────────────────────────────────────── echo "" -echo "[5/6] Pre-building backend demo..." +echo "[4/4] Pre-building backend..." dart run tools/build/build.dart backend -# ── 6. Playwright browsers (for website tests) ────────────────────── -echo "" -echo "[6/6] Installing Playwright browsers..." -(cd website && npx playwright install chromium) 2>/dev/null || echo " Playwright install skipped (optional)" - -# Ensure coverage tool is available -dart pub global activate coverage 2>/dev/null || true - echo "" echo "===========================================" echo " Setup complete!" echo "" -echo " Run the demo: ./run_dev.sh" +echo " Run the demo: sh examples/run_taskflow.sh" echo " Or manually: node examples/backend/build/server.js" echo " API at: http://localhost:3000" echo "===========================================" diff --git a/README.md b/README.md index 6c60615..3c7fd6b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,16 @@ Write your entire stack in Dart: React web apps, React Native mobile apps with E | [too-many-cooks](examples/too_many_cooks) | Multi-agent coordination MCP server ([npm](https://www.npmjs.com/package/too-many-cooks)) | | [Too Many Cooks VSCode](examples/too_many_cooks_vscode_extension) | VSCode extension for agent visualization | +## Dev Container (Recommended) + +This project includes a [Dev Container](.devcontainer/devcontainer.json) that provides a fully configured development environment with Dart, Node.js, and Chromium pre-installed. Open the project in VSCode and select **Reopen in Container** when prompted. + +**Why use it?** + +- **Consistent environment** — Dart 3.10, Node 20, and Chromium are pinned and pre-configured. No version mismatches across machines. +- **Avoids Windows + Node.js performance issues** — Node.js file-heavy operations (`npm install`, `dart2js` output, `node_modules` creation) run up to [4x slower on Windows than Linux](https://github.com/microsoft/Windows-Dev-Performance/issues/17) due to NTFS overhead and Windows Defender real-time scanning. Yo will probably get better performance running on [WSL2](https://docs.docker.com/desktop/features/wsl/). The Dev Container sidesteps this entirely by running inside a Linux container. +- **Zero setup** — Coverage thresholds, linting, and test tooling are pre-configured via environment variables and VSCode settings. + ## Quick Start ```bash @@ -36,7 +46,7 @@ Write your entire stack in Dart: React web apps, React Native mobile apps with E dart tools/switch_deps.dart local # Run everything -sh run_dev.sh +sh examples/run_taskflow.sh ``` Open http://localhost:8080/web/ diff --git a/README_zh.md b/README_zh.md index 050901a..e8712d7 100644 --- a/README_zh.md +++ b/README_zh.md @@ -36,7 +36,7 @@ dart tools/switch_deps.dart local # 运行全部 -sh run_dev.sh +sh examples/run_taskflow.sh ``` 打开 http://localhost:8080/web/ diff --git a/examples/README.md b/examples/README.md index 6222386..9dbdbfe 100644 --- a/examples/README.md +++ b/examples/README.md @@ -18,8 +18,8 @@ examples/ ## Quick Start ```bash -# Build everything from repo root -sh run_dev.sh +# Build and run the TaskFlow demo (Express + React + React Native) +sh examples/run_taskflow.sh ``` ## Too Many Cooks diff --git a/examples/run_taskflow.sh b/examples/run_taskflow.sh new file mode 100755 index 0000000..5c46e03 --- /dev/null +++ b/examples/run_taskflow.sh @@ -0,0 +1,75 @@ +#!/bin/bash +# Build and run the TaskFlow demo (Express + React + React Native) +# Dependencies are installed by .devcontainer/post-create.sh +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +cleanup() { + echo "" + echo "Shutting down servers..." + kill $SERVER_PID 2>/dev/null || true + kill $FRONTEND_PID 2>/dev/null || true + exit 0 +} + +trap cleanup INT TERM EXIT + +# Kill any existing instances by port +echo "Cleaning up old processes..." +lsof -ti :3000 | xargs kill -9 2>/dev/null || true +lsof -ti :8080 | xargs kill -9 2>/dev/null || true +sleep 2 + +echo "===================================" +echo " TaskFlow Development Environment" +echo "===================================" +echo "" + +# Step 1: Build all targets +echo "[1/2] Building all targets..." +cd "$ROOT_DIR" + +echo " Building backend..." +dart run tools/build/build.dart backend + +echo " Building mobile..." +dart run tools/build/build.dart mobile + +echo " Building frontend..." +cd "$SCRIPT_DIR/frontend" +mkdir -p build +dart compile js web/app.dart -o build/app.js -O2 + +# Step 2: Start servers +echo "" +echo "[2/2] Starting servers..." +echo "" + +cd "$SCRIPT_DIR/backend" +node build/server.js & +SERVER_PID=$! + +cd "$SCRIPT_DIR/frontend" +python3 -m http.server 8080 & +FRONTEND_PID=$! + +sleep 2 + +echo "===================================" +echo " Servers running!" +echo "===================================" +echo "" +echo " Backend API: http://localhost:3000" +echo " Frontend: http://localhost:8080/web/" +echo "" +echo " Mobile: Use VSCode launch config or run:" +echo " cd examples/mobile/rn && npm run ios" +echo " cd examples/mobile/rn && npm run android" +echo "" +echo " Press Ctrl+C to stop" +echo "===================================" +echo "" + +wait $SERVER_PID $FRONTEND_PID diff --git a/run_dev.sh b/run_dev.sh deleted file mode 100755 index c931059..0000000 --- a/run_dev.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash -set -e - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -cleanup() { - echo "" - echo "Shutting down servers..." - kill $SERVER_PID 2>/dev/null || true - kill $FRONTEND_PID 2>/dev/null || true - exit 0 -} - -trap cleanup SIGINT SIGTERM EXIT - -# Kill any existing instances by port (more reliable) -echo "Cleaning up old processes..." -lsof -ti :3000 | xargs kill -9 2>/dev/null || true -lsof -ti :8080 | xargs kill -9 2>/dev/null || true -sleep 2 - -echo "===================================" -echo " TaskFlow Development Environment" -echo "===================================" -echo "" - -# Step 1: Get dependencies for build tool -echo "[1/7] Setting up build tool..." -cd "$SCRIPT_DIR/tools/build" -dart pub get - -# Step 2: Get dependencies for all packages -echo "" -echo "[2/7] Getting dependencies for all packages..." -for pkg in "$SCRIPT_DIR/packages"/*; do - [ -f "$pkg/pubspec.yaml" ] && (cd "$pkg" && echo " $(basename "$pkg")..." && dart pub get) -done - -# Step 3: Get dependencies for all examples (Dart) -echo "" -echo "[3/7] Getting dependencies for all examples..." -for example in "$SCRIPT_DIR/examples"/*; do - [ -f "$example/pubspec.yaml" ] && (cd "$example" && echo " $(basename "$example")..." && dart pub get) -done - -# Step 4: npm install for backend -echo "" -echo "[4/7] Installing Node dependencies for backend..." -cd "$SCRIPT_DIR/examples/backend" -[ -d "node_modules" ] || npm install - -# Step 5: npm install for mobile/rn (Expo) -echo "" -echo "[5/7] Installing Node dependencies for mobile (Expo)..." -cd "$SCRIPT_DIR/examples/mobile/rn" -[ -d "node_modules" ] || npm install - -# Step 6: Build all targets -echo "" -echo "[6/7] Building all targets..." -cd "$SCRIPT_DIR" - -# Build backend -echo " Building backend..." -dart run tools/build/build.dart backend - -# Build mobile -echo " Building mobile..." -dart run tools/build/build.dart mobile - -# Build frontend (browser JS, not node preamble) -echo " Building frontend..." -cd "$SCRIPT_DIR/examples/frontend" -mkdir -p build -dart compile js web/app.dart -o build/app.js -O2 - -# Step 7: Start servers -echo "" -echo "[7/7] Starting servers..." -echo "" - -# Start Express backend on port 3000 -cd "$SCRIPT_DIR/examples/backend" -node build/server.js & -SERVER_PID=$! - -# Start simple HTTP server for frontend on port 8080 -cd "$SCRIPT_DIR/examples/frontend" -python3 -m http.server 8080 & -FRONTEND_PID=$! - -sleep 2 - -echo "===================================" -echo " Servers running!" -echo "===================================" -echo "" -echo " Backend API: http://localhost:3000" -echo " Frontend: http://localhost:8080/web/" -echo "" -echo " Mobile: Use VSCode launch config or run:" -echo " cd examples/mobile/rn && npm run ios" -echo " cd examples/mobile/rn && npm run android" -echo "" -echo " Press Ctrl+C to stop" -echo "===================================" -echo "" - -wait $SERVER_PID $FRONTEND_PID From 193657c67bb46a469ef6a16b576b7ef4b3d56608 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:24:23 +1100 Subject: [PATCH 04/20] Add skillz --- .claude/skills/build-extension/SKILL.md | 59 ++++++++++++++++ .claude/skills/create-pr/SKILL.md | 63 ++++++++++++++++++ .claude/skills/pub-get/SKILL.md | 30 +++++++++ .claude/skills/setup-claude/SKILL.md | 75 +++++++++++++++++++++ .claude/skills/setup-playwright/SKILL.md | 49 ++++++++++++++ .claude/skills/taskflow/SKILL.md | 52 +++++++++++++++ .claude/skills/test/SKILL.md | 85 ++++++++++++++++++++++++ .claude/skills/website/SKILL.md | 62 +++++++++++++++++ 8 files changed, 475 insertions(+) create mode 100644 .claude/skills/build-extension/SKILL.md create mode 100644 .claude/skills/create-pr/SKILL.md create mode 100644 .claude/skills/pub-get/SKILL.md create mode 100644 .claude/skills/setup-claude/SKILL.md create mode 100644 .claude/skills/setup-playwright/SKILL.md create mode 100644 .claude/skills/taskflow/SKILL.md create mode 100644 .claude/skills/test/SKILL.md create mode 100644 .claude/skills/website/SKILL.md diff --git a/.claude/skills/build-extension/SKILL.md b/.claude/skills/build-extension/SKILL.md new file mode 100644 index 0000000..a9da67c --- /dev/null +++ b/.claude/skills/build-extension/SKILL.md @@ -0,0 +1,59 @@ +--- +name: build-extension +description: Build, test, and package VSCode extensions in the dart_node repo +argument-hint: "[too-many-cooks|dart-node-vsix] [build|test|package|install]" +disable-model-invocation: true +allowed-tools: Bash +--- + +# Build VSCode Extensions + +This repo has two VSCode extension components: + +| Component | Location | What it is | +|-----------|----------|------------| +| **dart_node_vsix** | `packages/dart_node_vsix/` | The SDK package — Dart bindings for the VSCode extension API (commands, tree views, webviews, status bar, etc.) | +| **too_many_cooks** | `examples/too_many_cooks_vscode_extension/` | A concrete extension built with that SDK — multi-agent coordination UI | + +## too-many-cooks extension + +**Full build** (MCP server + extension + .vsix package): +```bash +bash examples/too_many_cooks_vscode_extension/build.sh +``` + +This does: +1. Compiles MCP server: `dart compile js` → `add_preamble.dart` → `server_node.js` +2. Compiles extension: `dart compile js` → `wrap-extension.js` bridge → `out/lib/extension.js` +3. Packages: `vsce package` → `.vsix` file + +**Test** (runs Mocha tests under a real VSCode instance): +```bash +cd examples/too_many_cooks_vscode_extension && npm run pretest && npm test +``` +On headless Linux, prefix with `xvfb-run -a`. + +**Install into VSCode**: +```bash +code --install-extension examples/too_many_cooks_vscode_extension/*.vsix +``` + +## dart_node_vsix SDK package + +**Test the SDK** (Dart tests compiled to JS, run in VSCode): +```bash +cd packages/dart_node_vsix && npm install && npm run compile && npm test +``` + +**What it provides** — Dart bindings for: +- `commands`, `window`, `workspace`, `statusBar` +- `TreeView`, `Webview`, `OutputChannel` +- `ExtensionContext`, `Disposable` +- `Mocha` test API bindings +- JS interop helpers (`Promise`, `EventEmitter`) + +## Architecture + +Both use the same pattern: Dart → `dart compile js` → wrapper script → VSCode-compatible JS module. + +The wrapper scripts (`scripts/wrap-extension.js`, `scripts/wrap-tests.js`) bridge dart2js output to VSCode's CommonJS `require`/`module.exports` system and inject polyfills (navigator, self) needed by dart2js async scheduling. diff --git a/.claude/skills/create-pr/SKILL.md b/.claude/skills/create-pr/SKILL.md new file mode 100644 index 0000000..c930232 --- /dev/null +++ b/.claude/skills/create-pr/SKILL.md @@ -0,0 +1,63 @@ +--- +name: create-pr +description: Create a pull request using the dart_node PR template +disable-model-invocation: true +allowed-tools: Bash, Read, Grep, Glob +--- + +# Create Pull Request + +Create a PR following the dart_node template and conventions. + +## Steps + +1. **Check state**: + ```bash + git status + git diff main...HEAD + git log main..HEAD --oneline + ``` + +2. **Analyze all commits** from the branch (NOT just the latest — ALL commits since diverging from main). Use `git diff main` to understand the full scope. + +3. **Draft PR using the template** from `.github/PULL_REQUEST_TEMPLATE.md`: + + ``` + ## TLDR; + [One sentence summary] + + ## What Does This Do? + [Clear description of the change] + + ## Brief Details? + [Implementation details, design decisions] + + ## How Do The Tests Prove The Change Works? + [Describe test coverage and what the tests verify] + ``` + +4. **Create the PR**: + ```bash + gh pr create --title "Short title under 70 chars" --body "$(cat <<'EOF' + ## TLDR; + ... + + ## What Does This Do? + ... + + ## Brief Details? + ... + + ## How Do The Tests Prove The Change Works? + ... + EOF + )" + ``` + +## Rules + +- Keep the title under 70 chars +- Keep documentation tight (per CLAUDE.md) +- Only diff against `main` — ignore commit messages +- Push to remote with `-u` flag if needed +- Return the PR URL when done diff --git a/.claude/skills/pub-get/SKILL.md b/.claude/skills/pub-get/SKILL.md new file mode 100644 index 0000000..d42caa8 --- /dev/null +++ b/.claude/skills/pub-get/SKILL.md @@ -0,0 +1,30 @@ +--- +name: pub-get +description: Install all Dart and npm dependencies in dependency order +disable-model-invocation: true +allowed-tools: Bash +--- + +# Install Dependencies + +Run the dependency installer that handles the tiered monorepo structure. + +```bash +./tools/pub_get.sh +``` + +## What it does + +Installs `dart pub get` and `npm install` across all packages and examples in dependency order: + +1. **Tier 1 (Core):** `dart_logging`, `dart_node_coverage`, `dart_node_core`, `reflux` +2. **Tier 2 (Packages):** `dart_jsx`, `dart_node_express`, `dart_node_ws`, `dart_node_better_sqlite3`, `dart_node_mcp`, `dart_node_react`, `dart_node_react_native` +3. **Tier 3 (Examples):** `frontend`, `markdown_editor`, `reflux_demo/web_counter`, `too_many_cooks`, `backend`, `mobile`, `jsx_demo` + +Order matters because Tier 2 depends on Tier 1, and Tier 3 depends on both. + +## After running + +Report any failures. If a package fails, it's usually because: +- A dependency in an earlier tier hasn't been published yet (use `dart run tools/switch_deps.dart local` for local dev) +- npm packages need `npm install` in a subdirectory (e.g., `examples/mobile/rn/`) diff --git a/.claude/skills/setup-claude/SKILL.md b/.claude/skills/setup-claude/SKILL.md new file mode 100644 index 0000000..262bca7 --- /dev/null +++ b/.claude/skills/setup-claude/SKILL.md @@ -0,0 +1,75 @@ +--- +name: setup-claude +description: Install Claude Code CLI and the Too Many Cooks VSCode extension for multi-agent development +disable-model-invocation: true +allowed-tools: Bash +--- + +# Install Claude Code & Extension + +Sets up Claude Code CLI and the Too Many Cooks VSCode extension for multi-agent coordination. + +## Step 1: Install Claude Code CLI + +```bash +npm install -g @anthropic-ai/claude-code +``` + +Verify: +```bash +claude --version +``` + +## Step 2: Build the Too Many Cooks VSCode Extension + +The extension provides visual agent coordination (locks, messages, plans) via the MCP server. + +### 2a. Build the MCP Server first + +```bash +cd examples/too_many_cooks && dart pub get && npm install +dart compile js -o examples/too_many_cooks/build/bin/server.js examples/too_many_cooks/bin/server.dart +dart run tools/build/add_preamble.dart \ + examples/too_many_cooks/build/bin/server.js \ + examples/too_many_cooks/build/bin/server_node.js \ + --shebang +``` + +### 2b. Build and package the extension + +```bash +cd examples/too_many_cooks_vscode_extension +npm install +npm run compile +npx @vscode/vsce package +``` + +This creates a `.vsix` file in `examples/too_many_cooks_vscode_extension/`. + +### 2c. Install the extension + +```bash +code --install-extension examples/too_many_cooks_vscode_extension/*.vsix +``` + +Or use the full build script that does steps 2a-2b: +```bash +bash examples/too_many_cooks_vscode_extension/build.sh +``` + +## Step 3: Configure Claude Code for this project + +The project's `.claude/settings.local.json` already has the required permissions: +- `Bash(dart pub get:*)` — dependency installation +- `mcp__too-many-cooks__register` — MCP agent registration +- `Bash(docker ps:*)` — Docker status checks + +Custom skills are in `.claude/skills/` — run `/help` to see them. + +## Multi-Agent Usage + +After setup, agents coordinate via the Too Many Cooks MCP server: +- **Lock files** before editing, unlock after +- **Check messages** regularly between agents +- **Update plans** so other agents can see your intent +- Keep your agent key — it's critical for authentication diff --git a/.claude/skills/setup-playwright/SKILL.md b/.claude/skills/setup-playwright/SKILL.md new file mode 100644 index 0000000..60b6326 --- /dev/null +++ b/.claude/skills/setup-playwright/SKILL.md @@ -0,0 +1,49 @@ +--- +name: setup-playwright +description: Install Chromium and Playwright for website E2E testing +disable-model-invocation: true +allowed-tools: Bash +--- + +# Install Chromium & Playwright + +Sets up Playwright with Chromium for running the website's E2E test suite. + +## Steps + +1. **Install website npm dependencies** (includes `@playwright/test`): + ```bash + cd website && npm ci + ``` + +2. **Install Chromium browser and OS dependencies**: + ```bash + cd website && npx playwright install --with-deps chromium + ``` + This installs the Chromium binary plus required system libraries (libgbm, libasound, etc.). + +3. **Verify installation**: + ```bash + cd website && npx playwright --version + ``` + +## Notes + +- Only Chromium is needed — this project does not test against Firefox or WebKit. +- On the Dev Container (Ubuntu 24.04), `--with-deps` installs the OS packages automatically. +- On macOS, Playwright downloads its own Chromium binary — no Homebrew needed. +- Browser cache lives at `~/.cache/ms-playwright/`. Delete this to force a clean reinstall. +- The website tests are at `website/tests/` and configured in `website/playwright.config.js`. +- Base URL: `http://localhost:8080` (Eleventy dev server). + +## After installing + +Run the website tests: +```bash +cd website && npm test +``` + +Or with UI mode: +```bash +cd website && npm run test:ui +``` diff --git a/.claude/skills/taskflow/SKILL.md b/.claude/skills/taskflow/SKILL.md new file mode 100644 index 0000000..973c51d --- /dev/null +++ b/.claude/skills/taskflow/SKILL.md @@ -0,0 +1,52 @@ +--- +name: taskflow +description: Build and run the full TaskFlow demo stack (Express backend + React frontend + mobile) +disable-model-invocation: true +allowed-tools: Bash +--- + +# TaskFlow Demo + +Build and run the full-stack demo that showcases dart_node's capabilities. + +## Run it + +```bash +sh examples/run_taskflow.sh +``` + +This: +1. Kills any existing processes on ports 3000 and 8080 +2. Builds backend, mobile, and frontend from Dart to JS +3. Starts the Express backend on **http://localhost:3000** +4. Starts the frontend dev server on **http://localhost:8080/web/** +5. Traps Ctrl+C for clean shutdown + +## Ports + +| Service | Port | How | +|---------|------|-----| +| Backend API (Express) | 3000 | `node examples/backend/build/server.js` | +| Frontend (static) | 8080 | `python3 -m http.server 8080` | +| Mobile (Expo) | — | `cd examples/mobile/rn && npm run ios` or `npm run android` | + +## Prerequisites + +Dependencies must be installed first. If the build fails, run: +```bash +./tools/pub_get.sh +``` + +Or use the Dev Container which runs `post-create.sh` automatically. + +## Manual start (individual services) + +Backend only: +```bash +dart run tools/build/build.dart backend && node examples/backend/build/server.js +``` + +Frontend only: +```bash +cd examples/frontend && bash build.sh && python3 -m http.server 8080 +``` diff --git a/.claude/skills/test/SKILL.md b/.claude/skills/test/SKILL.md new file mode 100644 index 0000000..d1015d3 --- /dev/null +++ b/.claude/skills/test/SKILL.md @@ -0,0 +1,85 @@ +--- +name: test +description: Run tests and coverage for dart_node packages. Automatically detects which packages to test based on changed files. +argument-hint: "[--tier N] [--all] [package...]" +disable-model-invocation: true +allowed-tools: Bash, Read, Grep, Glob +--- + +# Run Tests & Coverage + +## Step 1: Determine what to test + +If `$ARGUMENTS` specifies a tier or package, use that. Otherwise, **detect which packages were affected by recent changes**: + +```bash +git diff --name-only main +``` + +Map changed files to packages: + +| Changed path | Test target | +|---|---| +| `packages/dart_node_core/` | `packages/dart_node_core` | +| `packages/dart_node_express/` | `packages/dart_node_express` | +| `packages/dart_node_ws/` | `packages/dart_node_ws` | +| `packages/dart_node_better_sqlite3/` | `packages/dart_node_better_sqlite3` | +| `packages/dart_node_mcp/` | `packages/dart_node_mcp` | +| `packages/dart_node_react/` | `packages/dart_node_react` | +| `packages/dart_node_react_native/` | `packages/dart_node_react_native` | +| `packages/dart_logging/` | `packages/dart_logging` | +| `packages/reflux/` | `packages/reflux` | +| `packages/dart_jsx/` | `packages/dart_jsx` | +| `examples/frontend/` | `examples/frontend` | +| `examples/too_many_cooks/` | `examples/too_many_cooks` | +| `examples/markdown_editor/` | `examples/markdown_editor` | +| `examples/reflux_demo/` | `examples/reflux_demo/web_counter` | +| `tools/` or root config | Run `--all` | + +If a Tier 1 package changed, also run its downstream dependents in Tier 2/3. + +## Step 2: Run tests + +**Specific packages** (preferred — faster): +```bash +./tools/test.sh packages/dart_node_core packages/dart_node_express +``` + +**Full tier**: +```bash +./tools/test.sh --tier 1 +``` + +**Everything** (use `--all` arg or when root files changed): +```bash +./tools/test.sh +``` + +## Step 3: Coverage for affected packages + +For each Node package that was tested, run the coverage tool: + +```bash +cd packages/ && dart run ../dart_node_coverage/bin/coverage.dart +``` + +Node packages (use coverage CLI): `dart_node_core`, `dart_node_express`, `dart_node_ws`, `dart_node_better_sqlite3`, `dart_node_mcp`, `dart_node_react_native` + +Browser packages (use `dart test -p chrome`): `dart_node_react` + +Standard packages (use `dart test --coverage`): `dart_logging`, `reflux` + +Minimum coverage: **80%**. If below threshold, read `coverage/lcov.info` and identify uncovered lines. + +## Tiers reference + +- **Tier 1:** `dart_logging`, `dart_node_core` +- **Tier 2:** `reflux`, `dart_node_express`, `dart_node_ws`, `dart_node_better_sqlite3`, `dart_node_mcp`, `dart_node_react_native`, `dart_node_react` +- **Tier 3:** `examples/frontend`, `examples/markdown_editor`, `examples/reflux_demo/web_counter`, `examples/too_many_cooks` + +## After running + +1. Report PASS/FAIL per package with coverage percentages +2. If failures occur, read the log from `logs/.log` and diagnose +3. If coverage is below 80%, suggest specific test cases to add +4. Never skip tests, remove assertions, or silently swallow failures diff --git a/.claude/skills/website/SKILL.md b/.claude/skills/website/SKILL.md new file mode 100644 index 0000000..b87de43 --- /dev/null +++ b/.claude/skills/website/SKILL.md @@ -0,0 +1,62 @@ +--- +name: website +description: Build, serve, and test the dart_node documentation website (Eleventy + Playwright) +argument-hint: "[build|dev|test|clean]" +disable-model-invocation: true +allowed-tools: Bash +--- + +# Website (Eleventy + Playwright) + +Build and test the dart_node documentation site at `website/`. + +## Commands + +Parse `$ARGUMENTS` to determine the action: + +**build** — Full production build (copies READMEs, generates API docs, runs Eleventy): +```bash +cd website && npm run build +``` + +**dev** — Start the dev server with live reload: +```bash +cd website && npm run dev +``` +Serves at `http://localhost:8080`. + +**test** — Run Playwright E2E tests (builds first if `_site/` doesn't exist): +```bash +cd website && npm test +``` + +**test:coverage** — Run tests with V8 JS coverage: +```bash +cd website && npm run test:coverage +``` + +**clean** — Remove generated files: +```bash +cd website && npm run clean +``` + +**No args** — Default to `build` then `test`. + +## Build pipeline + +`npm run build` runs `scripts/build.sh` which does: +1. `node scripts/copy-readmes.js` — Copies package READMEs into the site +2. `bash scripts/generate-api-docs.sh` — Generates API reference from Dart source +3. `npx eleventy` — Generates static site into `_site/` + +## Test files + +9 test specs in `website/tests/`: +- Page content, navigation, language switching +- Theme toggle (dark/light mode) +- Mobile responsiveness +- Code blocks and syntax highlighting + +## Prerequisites + +Playwright must be installed first. Run `/setup-playwright` if `npx playwright --version` fails. From 733c61eed3eb7805d77ebdd92474a50edfdde3a3 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:28:24 +1100 Subject: [PATCH 05/20] Fix a dep --- AGENTS.md | 44 +++++++++++++------- packages/dart_node_express/package-lock.json | 7 ++-- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index cf9915b..7f6add4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,18 +1,20 @@ # CLAUDE.md -Dart packages for building Node.js apps. Typed Dart layer over JS interop. +Dart packages for building Node.js apps. Strongly Typed Dart layer over JS interop. + +## Rules + +⛔️ NEVER KILL (pkill) THE VSCODE PROCESS!!! +- Do not use Git unless asked by user ## Multi-Agent Coordination (Too Many Cooks) +- Keep your key! It's critical. Do not lose it! - Check messages regularly, lock files before editing, unlock after - Don't edit locked files; signal intent via plans and messages -- Coordinator: keep delegating via messages. Worker: keep asking for tasks via messages -- Clean up expired locks routinely - -## Code Rules **Language & Types** - All Dart, minimal JS. Use `dart:js_interop` (not deprecated `dart:js_util`/`package:js`) -- Never expose `JSObject`/`JSAny`/`dynamic` in public APIs—always typed +- AVOID `JSObject`/`JSAny`/`dynamic`! - Prefer typedef records over classes for data (structural typing) - ILLEGAL: `as`, `late`, `!`, `.then()`, global state @@ -21,17 +23,36 @@ Dart packages for building Node.js apps. Typed Dart layer over JS interop. - Return `Result` (nadz) instead of throwing exceptions - Functions < 20 lines, files < 500 LOC - Switch expressions/ternaries over if/else (except in declarative contexts) +- Where Typescript code exists with no Dart wrapper, create the Dart wrapper APIs and add to the appropriate packages. **Testing** - 100% coverage with high-level integration tests, not unit tests/mocks - Tests in separate files, not groups. Dart only (JS only for interop testing) -- Never skip tests. Never remove assertions. Failing tests OK, silent failures ILLEGAL +- Never skip tests. Never remove assertions. Failing tests OK, silent failures = ⛔️ ILLEGAL. Aggressively unskip tests. - NO PLACEHOLDERS—throw if incomplete **Dependencies** - All packages require: `austerity` (linting), `nadz` (Result types) - `node_preamble` for dart2js Node.js compatibility +**Pull Requests** +- Keep the documentation tight +- Use the template: .github/PULL_REQUEST_TEMPLATE.md +- Only use git diff with main. Ignore commit messages + +# Web & Translation + +- Optimize for AI Search and SEO +https://developers.google.com/search/blog/2025/05/succeeding-in-ai-search +https://developers.google.com/search/docs/fundamentals/seo-starter-guide + +- Always translate the English version to the target language directly. +- Be careful of cultural differences. +- Avoid literal translations that may offend the reader. +- Keep the code examples the same as the original but translate the comments to the target language +- Minimize CSS. +- Don't name CSS after sections. Name them after the HTML element + ## Codebase Structure ``` @@ -53,11 +74,4 @@ examples/ mobile/ # React Native example too_many_cooks/ # Multi-agent coordination server jsx_demo/ # JSX syntax demo -``` - -## Build & Test -```bash -dart run tools/build/build.dart # Build all -dart test # Run tests -dart analyze # Lint check -``` +``` \ No newline at end of file diff --git a/packages/dart_node_express/package-lock.json b/packages/dart_node_express/package-lock.json index d2b24f9..5582ebd 100644 --- a/packages/dart_node_express/package-lock.json +++ b/packages/dart_node_express/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "name": "dart_node_express", "dependencies": { "express": "^5.2.1" } @@ -580,9 +581,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" From 8910e168281829aa51aaaefb466330d4ce911f66 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:44:27 +1100 Subject: [PATCH 06/20] Upgrades --- .devcontainer/Dockerfile | 6 +- .devcontainer/devcontainer.json | 1 + examples/backend/package-lock.json | 134 ++++++++++++++++------ examples/too_many_cooks/package-lock.json | 42 ++++++- packages/dart_node_vsix/package-lock.json | 12 +- packages/dart_node_ws/package-lock.json | 7 +- 6 files changed, 147 insertions(+), 55 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 24a4c2c..de688d6 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,6 +6,7 @@ # │ │ # │ Dart SDK: 3.10.7 │ # │ Node.js: 20.20.0 │ +# │ npm: 11.8.0 │ # │ cspell: 9.6.0 │ # │ coverage: 1.15.0 │ # │ gh CLI: 2.67.0 (in devcontainer.json) │ @@ -14,6 +15,7 @@ FROM mcr.microsoft.com/devcontainers/base:ubuntu-24.04 ARG DART_VERSION=3.10.7 ARG NODE_VERSION=20.20.0 +ARG NPM_VERSION=11.8.0 ARG CSPELL_VERSION=9.6.0 ARG COVERAGE_VERSION=1.15.0 @@ -58,8 +60,8 @@ RUN set -eux; \ && rm /tmp/node.tar.xz \ && node --version && npm --version -# Global npm tools (pinned) -RUN npm install -g "cspell@${CSPELL_VERSION}" +# Upgrade npm itself (pinned), then global npm tools +RUN npm install -g "npm@${NPM_VERSION}" "cspell@${CSPELL_VERSION}" # Global Dart tools (pinned, as vscode user so pub-cache lands correctly) USER vscode diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 78f9653..a71da6b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,6 +5,7 @@ "args": { "DART_VERSION": "3.10.7", "NODE_VERSION": "20.20.0", + "NPM_VERSION": "11.8.0", "CSPELL_VERSION": "9.6.0", "COVERAGE_VERSION": "1.15.0" } diff --git a/examples/backend/package-lock.json b/examples/backend/package-lock.json index 2e8ad7e..3bdd2f6 100644 --- a/examples/backend/package-lock.json +++ b/examples/backend/package-lock.json @@ -30,29 +30,58 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -230,39 +259,39 @@ } }, "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", - "on-finished": "2.4.1", + "on-finished": "~2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", + "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", - "qs": "6.13.0", + "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", + "send": "~0.19.0", + "serve-static": "~1.16.2", "setprototypeof": "1.2.0", - "statuses": "2.0.1", + "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" @@ -573,12 +602,12 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -597,20 +626,49 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", diff --git a/examples/too_many_cooks/package-lock.json b/examples/too_many_cooks/package-lock.json index d6df992..579ca06 100644 --- a/examples/too_many_cooks/package-lock.json +++ b/examples/too_many_cooks/package-lock.json @@ -19,12 +19,25 @@ "node": ">=18.0.0" } }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.24.3", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.3.tgz", - "integrity": "sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==", + "version": "1.25.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz", + "integrity": "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==", "license": "MIT", "dependencies": { + "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", @@ -35,6 +48,7 @@ "express": "^5.0.1", "express-rate-limit": "^7.5.0", "jose": "^6.1.1", + "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", @@ -706,6 +720,16 @@ "node": ">= 0.4" } }, + "node_modules/hono": { + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -810,6 +834,12 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1054,9 +1084,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" diff --git a/packages/dart_node_vsix/package-lock.json b/packages/dart_node_vsix/package-lock.json index de0b84a..a97b2b8 100644 --- a/packages/dart_node_vsix/package-lock.json +++ b/packages/dart_node_vsix/package-lock.json @@ -1867,9 +1867,9 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3131,9 +3131,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true, "license": "MIT" }, diff --git a/packages/dart_node_ws/package-lock.json b/packages/dart_node_ws/package-lock.json index 8907977..40614e8 100644 --- a/packages/dart_node_ws/package-lock.json +++ b/packages/dart_node_ws/package-lock.json @@ -4,6 +4,7 @@ "requires": true, "packages": { "": { + "name": "dart_node_ws", "dependencies": { "express": "^5.2.1", "ws": "^8.18.3" @@ -581,9 +582,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" From 54c8c2d106d41c207d29257269ebf1e723c1d468 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:53:23 +1100 Subject: [PATCH 07/20] Fix skill --- .claude/skills/create-pr/SKILL.md | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/.claude/skills/create-pr/SKILL.md b/.claude/skills/create-pr/SKILL.md index c930232..cc55df8 100644 --- a/.claude/skills/create-pr/SKILL.md +++ b/.claude/skills/create-pr/SKILL.md @@ -18,23 +18,9 @@ Create a PR following the dart_node template and conventions. git log main..HEAD --oneline ``` -2. **Analyze all commits** from the branch (NOT just the latest — ALL commits since diverging from main). Use `git diff main` to understand the full scope. +2. **ignore all commits** do not pay attention to the commit messages -3. **Draft PR using the template** from `.github/PULL_REQUEST_TEMPLATE.md`: - - ``` - ## TLDR; - [One sentence summary] - - ## What Does This Do? - [Clear description of the change] - - ## Brief Details? - [Implementation details, design decisions] - - ## How Do The Tests Prove The Change Works? - [Describe test coverage and what the tests verify] - ``` +3. **Draft PR using the template** from `.github/PULL_REQUEST_TEMPLATE.md` 4. **Create the PR**: ```bash @@ -59,5 +45,4 @@ Create a PR following the dart_node template and conventions. - Keep the title under 70 chars - Keep documentation tight (per CLAUDE.md) - Only diff against `main` — ignore commit messages -- Push to remote with `-u` flag if needed - Return the PR URL when done From 0bc357f2393443c7072b82ddfbec95fd1092de61 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:58:11 +1100 Subject: [PATCH 08/20] spelling --- cspell-dictionary.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cspell-dictionary.txt b/cspell-dictionary.txt index 4909ae0..7cc9e0e 100644 --- a/cspell-dictionary.txt +++ b/cspell-dictionary.txt @@ -129,6 +129,7 @@ unconfigured # Project-specific christianfindlay Findlay +taskflow toomanycooks Nimblesite @@ -246,6 +247,7 @@ minimumlog # Other technical LTWH +NTFS blockquotes Blockquotes strikethrough From e66cb90ea89cd1f15d7865faabf896bc6a9368d0 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Tue, 27 Jan 2026 22:13:08 +1100 Subject: [PATCH 09/20] fixes --- examples/jsx_demo/lib/counter.g.dart | 30 ++++++------------- .../dart_node_coverage/lib/src/runtime.dart | 26 +++++++++++----- packages/dart_node_vsix/scripts/wrap-tests.js | 9 ++++++ .../test/suite/window_test.dart | 5 ++-- 4 files changed, 38 insertions(+), 32 deletions(-) diff --git a/examples/jsx_demo/lib/counter.g.dart b/examples/jsx_demo/lib/counter.g.dart index adc68eb..53e3c29 100644 --- a/examples/jsx_demo/lib/counter.g.dart +++ b/examples/jsx_demo/lib/counter.g.dart @@ -13,25 +13,13 @@ import 'package:dart_node_react/dart_node_react.dart'; ReactElement Counter() { final count = useState(0); - return $div(className: 'counter') >> - [ - $h1 >> 'Dart + JSX', - $div(className: 'value') >> count.value, - $div(className: 'buttons') >> - [ - $button( - className: 'btn-dec', - onClick: () => count.set(count.value - 1), - ) >> - '-', - $button( - className: 'btn-inc', - onClick: () => count.set(count.value + 1), - ) >> - '+', - ], - $div(className: 'buttons') >> - ($button(className: 'btn-reset', onClick: () => count.set(0)) >> - 'Reset'), - ]; + return $div(className: 'counter') >> [ + $h1 >> 'Dart + JSX', + $div(className: 'value') >> count.value, + $div(className: 'buttons') >> [ + $button(className: 'btn-dec', onClick: () => count.set(count.value - 1)) >> '-', + $button(className: 'btn-inc', onClick: () => count.set(count.value + 1)) >> '+', + ], + $div(className: 'buttons') >> ($button(className: 'btn-reset', onClick: () => count.set(0)) >> 'Reset'), +]; } diff --git a/packages/dart_node_coverage/lib/src/runtime.dart b/packages/dart_node_coverage/lib/src/runtime.dart index 6961474..37d1bfb 100644 --- a/packages/dart_node_coverage/lib/src/runtime.dart +++ b/packages/dart_node_coverage/lib/src/runtime.dart @@ -45,14 +45,17 @@ JSObject _getCoverageData() { /// Get or create file coverage object JSObject _getFileCoverage(String file) { final data = _getCoverageData(); - final existing = data.getProperty(file.toJS); + final fileJS = file.toJS; + final existing = data.getProperty(fileJS); + // Type-checked cast: safe after isA() verification if (existing != null && existing.isA()) { - return existing as JSObject; + return existing as JSObject; // Required for js_interop type narrowing } + // Create new file coverage object if it doesn't exist or is wrong type final newFile = JSObject(); - data.setProperty(file.toJS, newFile); + data.setProperty(fileJS, newFile); return newFile; } @@ -68,11 +71,14 @@ void cov(String file, int line) { final lineKey = line.toString().toJS; final currentRaw = fileCov.getProperty(lineKey); + // Fast path: increment existing count + // Type-checked cast: safe after isA() verification final double newCount; - if (currentRaw == null || !currentRaw.isA()) { - newCount = 1.0; + if (currentRaw != null && currentRaw.isA()) { + newCount = (currentRaw as JSNumber).toDartDouble + 1.0; // Required for js_interop } else { - newCount = (currentRaw as JSNumber).toDartDouble + 1.0; + // First execution of this line + newCount = 1.0; } fileCov.setProperty(lineKey, newCount.toJS); @@ -86,16 +92,19 @@ String getCoverageJson() { /// Write coverage data to a file (Node.js only) void writeCoverageFile(String outputPath) { + // Load Node.js modules final fsResult = _global.require.callAsFunction(null, 'fs'.toJS); if (fsResult == null || !fsResult.isA()) { - throw StateError('fs module not available'); + throw StateError('Failed to load fs module (Node.js required)'); } final pathResult = _global.require.callAsFunction(null, 'path'.toJS); if (pathResult == null || !pathResult.isA()) { - throw StateError('path module not available'); + throw StateError('Failed to load path module (Node.js required)'); } + // Type-checked casts: safe after isA() verification + // Extension type constructors require JSObject, casts are required for js_interop final fs = _NodeFS(fsResult as JSObject); final pathMod = _NodePath(pathResult as JSObject); @@ -106,6 +115,7 @@ void writeCoverageFile(String outputPath) { fs.mkdirSync(dir, options); } + // Write coverage data final jsonData = getCoverageJson(); fs.writeFileSync(outputPath.toJS, jsonData.toJS); } diff --git a/packages/dart_node_vsix/scripts/wrap-tests.js b/packages/dart_node_vsix/scripts/wrap-tests.js index f4b1d4f..576b0c9 100644 --- a/packages/dart_node_vsix/scripts/wrap-tests.js +++ b/packages/dart_node_vsix/scripts/wrap-tests.js @@ -36,6 +36,15 @@ for (const file of testFiles) { globalThis.vscode = require('vscode'); } + // Expose Mocha TDD functions on globalThis for @JS annotations + // These functions are defined by Mocha in the module context + if (typeof suite !== 'undefined') globalThis.suite = suite; + if (typeof test !== 'undefined') globalThis.test = test; + if (typeof suiteSetup !== 'undefined') globalThis.suiteSetup = suiteSetup; + if (typeof suiteTeardown !== 'undefined') globalThis.suiteTeardown = suiteTeardown; + if (typeof setup !== 'undefined') globalThis.setup = setup; + if (typeof teardown !== 'undefined') globalThis.teardown = teardown; + // Run the dart2js code ${dartJs} })(); diff --git a/packages/dart_node_vsix/test/suite/window_test.dart b/packages/dart_node_vsix/test/suite/window_test.dart index e269d53..b4223bc 100644 --- a/packages/dart_node_vsix/test/suite/window_test.dart +++ b/packages/dart_node_vsix/test/suite/window_test.dart @@ -26,10 +26,9 @@ void main() { // Note: We only test that the function exists and returns a promise. // We cannot await it because dialogs don't auto-dismiss in tests. final promise = vscode.window.showInformationMessage('Test message'); - // Promise is non-nullable but we test existence for API verification. - // ignore: unnecessary_null_comparison + // Test that it returns a valid JSPromise object assertOk( - promise != null, + (promise as JSAny).typeofEquals('object'), 'showInformationMessage should return promise', ); }), From 402fced9603934babe9ae394bff9c7b2192f9160 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 07:36:41 +1100 Subject: [PATCH 10/20] Try fix tests --- examples/markdown_editor/package-lock.json | 1060 ++++++++++++++++++ packages/dart_node_express/package-lock.json | 1 - packages/dart_node_ws/package-lock.json | 1 - tools/test.sh | 23 +- 4 files changed, 1075 insertions(+), 10 deletions(-) create mode 100644 examples/markdown_editor/package-lock.json diff --git a/examples/markdown_editor/package-lock.json b/examples/markdown_editor/package-lock.json new file mode 100644 index 0000000..e30adda --- /dev/null +++ b/examples/markdown_editor/package-lock.json @@ -0,0 +1,1060 @@ +{ + "name": "markdown-editor", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "markdown-editor", + "version": "0.1.0", + "devDependencies": { + "serve": "^14.2.1" + } + }, + "node_modules/@zeit/schemas": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "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/boxen": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/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/chalk-template/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/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", + "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.1.0", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/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/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-port-reachable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "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/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "dev": true, + "license": "MIT" + }, + "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/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/serve": { + "version": "14.2.5", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.5.tgz", + "integrity": "sha512-Qn/qMkzCcMFVPb60E/hQy+iRLpiU8PamOfOSYoAHmmF+fFFmpPpqa6Oci2iWYpTdOUM3VF+TINud7CfbQnsZbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@zeit/schemas": "2.36.0", + "ajv": "8.12.0", + "arg": "5.0.2", + "boxen": "7.0.0", + "chalk": "5.0.1", + "chalk-template": "0.4.0", + "clipboardy": "3.0.0", + "compression": "1.8.1", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.6", + "update-check": "1.5.4" + }, + "bin": { + "serve": "build/main.js" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/serve-handler": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } + }, + "node_modules/serve-handler/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-check": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" + } + }, + "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/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.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/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + } + } +} diff --git a/packages/dart_node_express/package-lock.json b/packages/dart_node_express/package-lock.json index 5582ebd..88566cb 100644 --- a/packages/dart_node_express/package-lock.json +++ b/packages/dart_node_express/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "dart_node_express", "dependencies": { "express": "^5.2.1" } diff --git a/packages/dart_node_ws/package-lock.json b/packages/dart_node_ws/package-lock.json index 40614e8..e94c984 100644 --- a/packages/dart_node_ws/package-lock.json +++ b/packages/dart_node_ws/package-lock.json @@ -4,7 +4,6 @@ "requires": true, "packages": { "": { - "name": "dart_node_ws", "dependencies": { "express": "^5.2.1", "ws": "^8.18.3" diff --git a/tools/test.sh b/tools/test.sh index 7328b83..687eab4 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -88,33 +88,38 @@ test_package() { cd "$full_path" + # Clear log file + > "$log" + + echo "=== Testing $name ===" | tee -a "$log" + # Build first if needed if is_type "$dir" "$BUILD_FIRST" && [[ -f "build.sh" ]]; then - ./build.sh >> "$log" 2>&1 || { echo "FAIL $name (build)"; return 1; } + ./build.sh 2>&1 | tee -a "$log" || { echo "FAIL $name (build)"; return 1; } fi # Install npm deps if needed - [[ -f "package.json" ]] && npm install >> "$log" 2>&1 + [[ -f "package.json" ]] && npm install 2>&1 | tee -a "$log" local coverage="" if is_type "$dir" "$NPM_PACKAGES"; then - npm test >> "$log" 2>&1 || { echo "FAIL $name"; return 1; } + npm test 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } elif is_type "$dir" "$NODE_INTEROP_PACKAGES"; then # Node interop packages: use coverage CLI like NODE_PACKAGES - dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { echo "FAIL $name"; return 1; } + dart run "$COVERAGE_CLI" 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } coverage=$(calc_coverage "coverage/lcov.info") elif is_type "$dir" "$NODE_PACKAGES"; then - dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { echo "FAIL $name"; return 1; } + dart run "$COVERAGE_CLI" 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } coverage=$(calc_coverage "coverage/lcov.info") elif is_type "$dir" "$BROWSER_PACKAGES"; then # Browser packages: run Chrome tests, check coverage if lcov.info exists - dart test -p chrome >> "$log" 2>&1 || { echo "FAIL $name"; return 1; } + dart test -p chrome 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } [[ -f "coverage/lcov.info" ]] && coverage=$(calc_coverage "coverage/lcov.info") else # Standard VM package with coverage - dart test --coverage=coverage >> "$log" 2>&1 || { echo "FAIL $name"; return 1; } - dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib >> "$log" 2>&1 + dart test --coverage=coverage 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } + dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib 2>&1 | tee -a "$log" coverage=$(calc_coverage "coverage/lcov.info") fi @@ -141,8 +146,10 @@ run_parallel() { ( if test_package "$dir"; then touch "$results_dir/$name.pass" + exit 0 else touch "$results_dir/$name.fail" + exit 1 fi ) & pids+=($!) From ae53b94c34c16e69bd809fa0d2e6bb622248c20b Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 07:39:41 +1100 Subject: [PATCH 11/20] Format --- examples/jsx_demo/lib/counter.g.dart | 30 +++++++++++++------ .../dart_node_coverage/lib/src/runtime.dart | 5 ++-- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/examples/jsx_demo/lib/counter.g.dart b/examples/jsx_demo/lib/counter.g.dart index 53e3c29..adc68eb 100644 --- a/examples/jsx_demo/lib/counter.g.dart +++ b/examples/jsx_demo/lib/counter.g.dart @@ -13,13 +13,25 @@ import 'package:dart_node_react/dart_node_react.dart'; ReactElement Counter() { final count = useState(0); - return $div(className: 'counter') >> [ - $h1 >> 'Dart + JSX', - $div(className: 'value') >> count.value, - $div(className: 'buttons') >> [ - $button(className: 'btn-dec', onClick: () => count.set(count.value - 1)) >> '-', - $button(className: 'btn-inc', onClick: () => count.set(count.value + 1)) >> '+', - ], - $div(className: 'buttons') >> ($button(className: 'btn-reset', onClick: () => count.set(0)) >> 'Reset'), -]; + return $div(className: 'counter') >> + [ + $h1 >> 'Dart + JSX', + $div(className: 'value') >> count.value, + $div(className: 'buttons') >> + [ + $button( + className: 'btn-dec', + onClick: () => count.set(count.value - 1), + ) >> + '-', + $button( + className: 'btn-inc', + onClick: () => count.set(count.value + 1), + ) >> + '+', + ], + $div(className: 'buttons') >> + ($button(className: 'btn-reset', onClick: () => count.set(0)) >> + 'Reset'), + ]; } diff --git a/packages/dart_node_coverage/lib/src/runtime.dart b/packages/dart_node_coverage/lib/src/runtime.dart index 37d1bfb..180e495 100644 --- a/packages/dart_node_coverage/lib/src/runtime.dart +++ b/packages/dart_node_coverage/lib/src/runtime.dart @@ -50,7 +50,7 @@ JSObject _getFileCoverage(String file) { // Type-checked cast: safe after isA() verification if (existing != null && existing.isA()) { - return existing as JSObject; // Required for js_interop type narrowing + return existing as JSObject; // Required for js_interop type narrowing } // Create new file coverage object if it doesn't exist or is wrong type @@ -75,7 +75,8 @@ void cov(String file, int line) { // Type-checked cast: safe after isA() verification final double newCount; if (currentRaw != null && currentRaw.isA()) { - newCount = (currentRaw as JSNumber).toDartDouble + 1.0; // Required for js_interop + newCount = + (currentRaw as JSNumber).toDartDouble + 1.0; // Required for js_interop } else { // First execution of this line newCount = 1.0; From 5c0091e0b102906d9d0023e7412befbb76331dc2 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 09:12:44 +1100 Subject: [PATCH 12/20] Script fixes --- .github/workflows/ci.yml | 14 +++++++++--- tools/test.sh | 49 ++++++++-------------------------------- 2 files changed, 20 insertions(+), 43 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b31980..2fadc4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,13 +113,21 @@ jobs: done - name: Test Tier 1 - run: ./tools/test.sh --ci --tier 1 + run: ./tools/test.sh --tier 1 - name: Test Tier 2 - run: ./tools/test.sh --ci --tier 2 + run: ./tools/test.sh --tier 2 - name: Test Tier 3 - run: ./tools/test.sh --ci --tier 3 + run: ./tools/test.sh --tier 3 + + - name: Upload test logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-logs + path: logs/ + retention-days: 7 too-many-cooks-mcp: name: Too Many Cooks MCP Server diff --git a/tools/test.sh b/tools/test.sh index 687eab4..c0b8ef3 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -1,10 +1,9 @@ #!/bin/bash # Unified test runner - parallel execution with fail-fast and coverage -# Usage: ./tools/test.sh [--tier N] [--ci] [package...] +# Usage: ./tools/test.sh [--tier N] [package...] # # Options: # --tier N Only run tier N (1, 2, or 3) -# --ci CI mode: fail-fast, minimal output # package... Specific packages/examples to test # # Without arguments: runs all packages and examples @@ -32,13 +31,11 @@ TIER2="packages/reflux packages/dart_node_express packages/dart_node_ws packages TIER3="examples/frontend examples/markdown_editor examples/reflux_demo/web_counter examples/too_many_cooks" # Parse arguments -CI_MODE=false TIER="" PACKAGES=() while [[ $# -gt 0 ]]; do case $1 in - --ci) CI_MODE=true; shift ;; --tier) TIER="$2"; shift 2 ;; *) PACKAGES+=("$1"); shift ;; esac @@ -99,7 +96,7 @@ test_package() { fi # Install npm deps if needed - [[ -f "package.json" ]] && npm install 2>&1 | tee -a "$log" + [[ -f "package.json" ]] && npm install --verbose 2>&1 | tee -a "$log" local coverage="" @@ -114,11 +111,11 @@ test_package() { coverage=$(calc_coverage "coverage/lcov.info") elif is_type "$dir" "$BROWSER_PACKAGES"; then # Browser packages: run Chrome tests, check coverage if lcov.info exists - dart test -p chrome 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } + dart test -p chrome --reporter expanded --fail-fast 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } [[ -f "coverage/lcov.info" ]] && coverage=$(calc_coverage "coverage/lcov.info") else # Standard VM package with coverage - dart test --coverage=coverage 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } + dart test --coverage=coverage --reporter expanded --fail-fast 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib 2>&1 | tee -a "$log" coverage=$(calc_coverage "coverage/lcov.info") fi @@ -136,49 +133,21 @@ test_package() { return 0 } -# Run tests in parallel, fail-fast on first failure +# Run tests in parallel run_parallel() { local pids=() - local results_dir=$(mktemp -d) for dir in "${TEST_PATHS[@]}"; do - local name=$(basename "$dir") - ( - if test_package "$dir"; then - touch "$results_dir/$name.pass" - exit 0 - else - touch "$results_dir/$name.fail" - exit 1 - fi - ) & + test_package "$dir" & pids+=($!) done - # Wait and check for failures + # Wait for all and fail if any failed local failed=0 - for i in "${!pids[@]}"; do - local pid=${pids[$i]} - local dir=${TEST_PATHS[$i]} - local name=$(basename "$dir") - - if ! wait "$pid"; then - failed=1 - if $CI_MODE; then - # Kill remaining processes on first failure - for p in "${pids[@]}"; do - kill "$p" 2>/dev/null || true - done - # Show error log - echo "" - echo "=== Error log for $name ===" - cat "$LOGS_DIR/$name.log" 2>/dev/null || true - break - fi - fi + for pid in "${pids[@]}"; do + wait "$pid" || failed=1 done - rm -rf "$results_dir" return $failed } From 3a8db0c635138a082e33fd8742c001a95686d72b Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 09:31:47 +1100 Subject: [PATCH 13/20] Exclude tmc --- tools/test.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/test.sh b/tools/test.sh index c0b8ef3..11c0037 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -30,6 +30,9 @@ TIER1="packages/dart_logging packages/dart_node_core" TIER2="packages/reflux packages/dart_node_express packages/dart_node_ws packages/dart_node_better_sqlite3 packages/dart_node_mcp packages/dart_node_react_native packages/dart_node_react" TIER3="examples/frontend examples/markdown_editor examples/reflux_demo/web_counter examples/too_many_cooks" +# Exclusion list (package names to skip) +EXCLUDED="too_many_cooks too_many_cooks_vscode_extension" + # Parse arguments TIER="" PACKAGES=() @@ -59,6 +62,15 @@ else TEST_PATHS=("${T1[@]}" "${T2[@]}" "${T3[@]}") fi +# Filter out excluded packages +FILTERED_PATHS=() +for path in "${TEST_PATHS[@]}"; do + if ! is_excluded "$path"; then + FILTERED_PATHS+=("$path") + fi +done +TEST_PATHS=("${FILTERED_PATHS[@]}") + mkdir -p "$LOGS_DIR" # Helper functions @@ -68,6 +80,11 @@ is_type() { [[ " $list " =~ " $name " ]] } +is_excluded() { + local name=$(basename "$1") + [[ " $EXCLUDED " =~ " $name " ]] +} + calc_coverage() { local lcov="$1" [[ -f "$lcov" ]] || { echo "0"; return; } @@ -153,6 +170,7 @@ run_parallel() { # Main echo "Testing ${#TEST_PATHS[@]} packages (MIN_COVERAGE=${MIN_COVERAGE}%)" +echo "Excluded: $EXCLUDED" echo "Logs: $LOGS_DIR/" echo "" From 52773cd7ca0d521f483f99210eb02ccda006d2da Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:33:09 +1100 Subject: [PATCH 14/20] Fix script --- tools/test.sh | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tools/test.sh b/tools/test.sh index 11c0037..6193d11 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -33,6 +33,24 @@ TIER3="examples/frontend examples/markdown_editor examples/reflux_demo/web_count # Exclusion list (package names to skip) EXCLUDED="too_many_cooks too_many_cooks_vscode_extension" +# Helper functions +is_type() { + local name=$(basename "$1") + local list="$2" + [[ " $list " =~ " $name " ]] +} + +is_excluded() { + local name=$(basename "$1") + [[ " $EXCLUDED " =~ " $name " ]] +} + +calc_coverage() { + local lcov="$1" + [[ -f "$lcov" ]] || { echo "0"; return; } + awk -F: '/^LF:/ { total += $2 } /^LH:/ { covered += $2 } END { if (total > 0) printf "%.1f", (covered / total) * 100; else print "0" }' "$lcov" +} + # Parse arguments TIER="" PACKAGES=() @@ -73,24 +91,6 @@ TEST_PATHS=("${FILTERED_PATHS[@]}") mkdir -p "$LOGS_DIR" -# Helper functions -is_type() { - local name=$(basename "$1") - local list="$2" - [[ " $list " =~ " $name " ]] -} - -is_excluded() { - local name=$(basename "$1") - [[ " $EXCLUDED " =~ " $name " ]] -} - -calc_coverage() { - local lcov="$1" - [[ -f "$lcov" ]] || { echo "0"; return; } - awk -F: '/^LF:/ { total += $2 } /^LH:/ { covered += $2 } END { if (total > 0) printf "%.1f", (covered / total) * 100; else print "0" }' "$lcov" -} - # Test a single package (runs in subshell) test_package() { local dir="$1" From a4d2e6194e6a2b2b3be15647117da7ff755951d4 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 11:53:19 +1100 Subject: [PATCH 15/20] fix script --- tools/test.sh | 177 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 130 insertions(+), 47 deletions(-) diff --git a/tools/test.sh b/tools/test.sh index 6193d11..dbb8346 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -62,33 +62,29 @@ while [[ $# -gt 0 ]]; do esac done -# Determine what to test +# Determine what to test (as tiers) +TIERS_TO_RUN=() + if [[ ${#PACKAGES[@]} -gt 0 ]]; then - TEST_PATHS=("${PACKAGES[@]}") + # Specific packages - run as single tier + TIERS_TO_RUN+=("${PACKAGES[*]}") elif [[ -n "$TIER" ]]; then + # Single tier case $TIER in - 1) read -ra TEST_PATHS <<< "$TIER1" ;; - 2) read -ra TEST_PATHS <<< "$TIER2" ;; - 3) read -ra TEST_PATHS <<< "$TIER3" ;; + 1) TIERS_TO_RUN+=("$TIER1") ;; + 2) TIERS_TO_RUN+=("$TIER2") ;; + 3) TIERS_TO_RUN+=("$TIER3") ;; *) echo "Invalid tier: $TIER"; exit 1 ;; esac else - # All tiers - read -ra T1 <<< "$TIER1" - read -ra T2 <<< "$TIER2" - read -ra T3 <<< "$TIER3" - TEST_PATHS=("${T1[@]}" "${T2[@]}" "${T3[@]}") + # All tiers - run sequentially + TIERS_TO_RUN+=("$TIER1") + TIERS_TO_RUN+=("$TIER2") + TIERS_TO_RUN+=("$TIER3") fi -# Filter out excluded packages -FILTERED_PATHS=() -for path in "${TEST_PATHS[@]}"; do - if ! is_excluded "$path"; then - FILTERED_PATHS+=("$path") - fi -done -TEST_PATHS=("${FILTERED_PATHS[@]}") - +# Clean and recreate logs directory +rm -rf "$LOGS_DIR" mkdir -p "$LOGS_DIR" # Test a single package (runs in subshell) @@ -105,81 +101,168 @@ test_package() { # Clear log file > "$log" - echo "=== Testing $name ===" | tee -a "$log" + echo "🏁 Starting $name" + echo "=== Testing $name ===" >> "$log" # Build first if needed if is_type "$dir" "$BUILD_FIRST" && [[ -f "build.sh" ]]; then - ./build.sh 2>&1 | tee -a "$log" || { echo "FAIL $name (build)"; return 1; } + ./build.sh >> "$log" 2>&1 || { echo "⛔️ Failed $name (build)" ; return 1; } fi # Install npm deps if needed - [[ -f "package.json" ]] && npm install --verbose 2>&1 | tee -a "$log" + [[ -f "package.json" ]] && npm install --silent >> "$log" 2>&1 local coverage="" if is_type "$dir" "$NPM_PACKAGES"; then - npm test 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } + npm test >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } elif is_type "$dir" "$NODE_INTEROP_PACKAGES"; then # Node interop packages: use coverage CLI like NODE_PACKAGES - dart run "$COVERAGE_CLI" 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } + dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } coverage=$(calc_coverage "coverage/lcov.info") elif is_type "$dir" "$NODE_PACKAGES"; then - dart run "$COVERAGE_CLI" 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } + dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } coverage=$(calc_coverage "coverage/lcov.info") elif is_type "$dir" "$BROWSER_PACKAGES"; then # Browser packages: run Chrome tests, check coverage if lcov.info exists - dart test -p chrome --reporter expanded --fail-fast 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } + dart test -p chrome --reporter expanded --fail-fast >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } [[ -f "coverage/lcov.info" ]] && coverage=$(calc_coverage "coverage/lcov.info") else # Standard VM package with coverage - dart test --coverage=coverage --reporter expanded --fail-fast 2>&1 | tee -a "$log" || { echo "FAIL $name"; return 1; } - dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib 2>&1 | tee -a "$log" + dart test --coverage=coverage --reporter expanded --fail-fast >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } + dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib >> "$log" 2>&1 coverage=$(calc_coverage "coverage/lcov.info") fi # Check coverage threshold if applicable if [[ -n "$coverage" ]]; then if [[ "$coverage" == "0" ]] || (( $(echo "$coverage < $MIN_COVERAGE" | bc -l) )); then - echo "FAIL $name (coverage ${coverage}% < ${MIN_COVERAGE}%)" + echo "⛔️ Failed $name (coverage ${coverage}% < ${MIN_COVERAGE}%)" return 1 fi - echo "PASS $name (${coverage}%)" + echo "✅ Succeeded $name (${coverage}%)" else - echo "PASS $name" + echo "✅ Succeeded $name" fi return 0 } -# Run tests in parallel -run_parallel() { +# Extract failure summary from a log file +extract_failure() { + local log="$1" + local name="$2" + + # Look for "Failed to load" or "Failed to run" errors + if grep -q "Failed to load\|Failed to run\|Some tests failed" "$log"; then + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "FAILURE: $name" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + # Extract failed test file names + grep "Failed to load\|Failed to run" "$log" | head -5 | while IFS= read -r line; do + if [[ "$line" =~ Failed\ to\ load\ \"([^\"]+)\" ]]; then + echo " Test: ${BASH_REMATCH[1]}" + fi + done + + # Extract first error message + echo "" + echo " Error:" + grep -A 1 "Failed to load\|Failed to run" "$log" | head -3 | sed 's/^/ /' + echo "" + echo " Full log: $log" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + fi +} + +# Run a tier of tests in parallel (wait for all, don't kill on failure) +run_tier() { + local tier_paths=("$@") local pids=() + local failed_packages=() + + # Filter out excluded packages + local filtered=() + for path in "${tier_paths[@]}"; do + if ! is_excluded "$path"; then + filtered+=("$path") + fi + done - for dir in "${TEST_PATHS[@]}"; do + [[ ${#filtered[@]} -eq 0 ]] && return 0 + + # Start all tests in parallel + for dir in "${filtered[@]}"; do test_package "$dir" & pids+=($!) done - # Wait for all and fail if any failed - local failed=0 - for pid in "${pids[@]}"; do - wait "$pid" || failed=1 + # Wait for ALL jobs to complete, track failures + for i in "${!pids[@]}"; do + if ! wait "${pids[$i]}"; then + failed_packages+=("${filtered[$i]}") + fi done - return $failed + # Report failures with details + if [[ ${#failed_packages[@]} -gt 0 ]]; then + echo "" + echo "╔════════════════════════════════════════════════════════════════╗" + echo "║ ${#failed_packages[@]} PACKAGE(S) FAILED" + echo "╚════════════════════════════════════════════════════════════════╝" + + for dir in "${failed_packages[@]}"; do + local name=$(basename "$dir") + local log="$LOGS_DIR/$name.log" + extract_failure "$log" "$name" + done + + return 1 + fi + + return 0 } # Main -echo "Testing ${#TEST_PATHS[@]} packages (MIN_COVERAGE=${MIN_COVERAGE}%)" +echo "Running ${#TIERS_TO_RUN[@]} tier(s) (MIN_COVERAGE=${MIN_COVERAGE}%)" echo "Excluded: $EXCLUDED" echo "Logs: $LOGS_DIR/" echo "" -if run_parallel; then - echo "" - echo "All tests passed" - exit 0 +# Determine actual tier number for display +if [[ -n "$TIER" ]]; then + TIER_LABEL="TIER $TIER" +elif [[ ${#PACKAGES[@]} -gt 0 ]]; then + TIER_LABEL="CUSTOM" else - echo "" - echo "Tests failed" - exit 1 + TIER_LABEL="ALL TIERS" fi + +tier_num=1 +for tier_spec in "${TIERS_TO_RUN[@]}"; do + read -ra tier_paths <<< "$tier_spec" + + # Display label + if [[ "$TIER_LABEL" == "ALL TIERS" ]]; then + echo "=== TIER $tier_num: ${#tier_paths[@]} packages ===" + else + echo "=== $TIER_LABEL: ${#tier_paths[@]} packages ===" + fi + + if ! run_tier "${tier_paths[@]}"; then + echo "" + if [[ "$TIER_LABEL" == "ALL TIERS" ]]; then + echo "TIER $tier_num FAILED - stopping" + else + echo "$TIER_LABEL FAILED" + fi + exit 1 + fi + + echo "" + ((tier_num++)) +done + +echo "All tests passed" +exit 0 From 086c5c1fcc1fa397ad34d5e0eaea57aa1a30b31b Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:08:54 +1100 Subject: [PATCH 16/20] Format time --- .claude/skills/setup-claude/SKILL.md | 75 ------------ .claude/skills/setup-playwright/SKILL.md | 49 -------- .claude/skills/setup/SKILL.md | 130 +++++++++++++++++++++ packages/dart_node_react/package-lock.json | 6 + tools/test.sh | 78 +++++++++++-- 5 files changed, 202 insertions(+), 136 deletions(-) delete mode 100644 .claude/skills/setup-claude/SKILL.md delete mode 100644 .claude/skills/setup-playwright/SKILL.md create mode 100644 .claude/skills/setup/SKILL.md create mode 100644 packages/dart_node_react/package-lock.json diff --git a/.claude/skills/setup-claude/SKILL.md b/.claude/skills/setup-claude/SKILL.md deleted file mode 100644 index 262bca7..0000000 --- a/.claude/skills/setup-claude/SKILL.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -name: setup-claude -description: Install Claude Code CLI and the Too Many Cooks VSCode extension for multi-agent development -disable-model-invocation: true -allowed-tools: Bash ---- - -# Install Claude Code & Extension - -Sets up Claude Code CLI and the Too Many Cooks VSCode extension for multi-agent coordination. - -## Step 1: Install Claude Code CLI - -```bash -npm install -g @anthropic-ai/claude-code -``` - -Verify: -```bash -claude --version -``` - -## Step 2: Build the Too Many Cooks VSCode Extension - -The extension provides visual agent coordination (locks, messages, plans) via the MCP server. - -### 2a. Build the MCP Server first - -```bash -cd examples/too_many_cooks && dart pub get && npm install -dart compile js -o examples/too_many_cooks/build/bin/server.js examples/too_many_cooks/bin/server.dart -dart run tools/build/add_preamble.dart \ - examples/too_many_cooks/build/bin/server.js \ - examples/too_many_cooks/build/bin/server_node.js \ - --shebang -``` - -### 2b. Build and package the extension - -```bash -cd examples/too_many_cooks_vscode_extension -npm install -npm run compile -npx @vscode/vsce package -``` - -This creates a `.vsix` file in `examples/too_many_cooks_vscode_extension/`. - -### 2c. Install the extension - -```bash -code --install-extension examples/too_many_cooks_vscode_extension/*.vsix -``` - -Or use the full build script that does steps 2a-2b: -```bash -bash examples/too_many_cooks_vscode_extension/build.sh -``` - -## Step 3: Configure Claude Code for this project - -The project's `.claude/settings.local.json` already has the required permissions: -- `Bash(dart pub get:*)` — dependency installation -- `mcp__too-many-cooks__register` — MCP agent registration -- `Bash(docker ps:*)` — Docker status checks - -Custom skills are in `.claude/skills/` — run `/help` to see them. - -## Multi-Agent Usage - -After setup, agents coordinate via the Too Many Cooks MCP server: -- **Lock files** before editing, unlock after -- **Check messages** regularly between agents -- **Update plans** so other agents can see your intent -- Keep your agent key — it's critical for authentication diff --git a/.claude/skills/setup-playwright/SKILL.md b/.claude/skills/setup-playwright/SKILL.md deleted file mode 100644 index 60b6326..0000000 --- a/.claude/skills/setup-playwright/SKILL.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: setup-playwright -description: Install Chromium and Playwright for website E2E testing -disable-model-invocation: true -allowed-tools: Bash ---- - -# Install Chromium & Playwright - -Sets up Playwright with Chromium for running the website's E2E test suite. - -## Steps - -1. **Install website npm dependencies** (includes `@playwright/test`): - ```bash - cd website && npm ci - ``` - -2. **Install Chromium browser and OS dependencies**: - ```bash - cd website && npx playwright install --with-deps chromium - ``` - This installs the Chromium binary plus required system libraries (libgbm, libasound, etc.). - -3. **Verify installation**: - ```bash - cd website && npx playwright --version - ``` - -## Notes - -- Only Chromium is needed — this project does not test against Firefox or WebKit. -- On the Dev Container (Ubuntu 24.04), `--with-deps` installs the OS packages automatically. -- On macOS, Playwright downloads its own Chromium binary — no Homebrew needed. -- Browser cache lives at `~/.cache/ms-playwright/`. Delete this to force a clean reinstall. -- The website tests are at `website/tests/` and configured in `website/playwright.config.js`. -- Base URL: `http://localhost:8080` (Eleventy dev server). - -## After installing - -Run the website tests: -```bash -cd website && npm test -``` - -Or with UI mode: -```bash -cd website && npm run test:ui -``` diff --git a/.claude/skills/setup/SKILL.md b/.claude/skills/setup/SKILL.md new file mode 100644 index 0000000..e20ab8a --- /dev/null +++ b/.claude/skills/setup/SKILL.md @@ -0,0 +1,130 @@ +--- +name: setup +description: Install development tools (claude | playwright) +disable-model-invocation: true +allowed-tools: Bash +--- + +# Setup Development Tools + +Install Claude Code CLI with Too Many Cooks extension OR Playwright with Chromium. + +**Usage**: `/setup ` +- `claude` — Install Claude Code CLI and Too Many Cooks VSCode extension +- `playwright` — Install Chromium and Playwright for website E2E testing + +--- + +## Claude Setup + +Sets up Claude Code CLI and the Too Many Cooks VSCode extension for multi-agent coordination. + +### Step 1: Install Claude Code CLI + +```bash +npm install -g @anthropic-ai/claude-code +``` + +Verify: +```bash +claude --version +``` + +### Step 2: Build the Too Many Cooks VSCode Extension + +The extension provides visual agent coordination (locks, messages, plans) via the MCP server. + +#### 2a. Build the MCP Server first + +```bash +cd examples/too_many_cooks && dart pub get && npm install +dart compile js -o examples/too_many_cooks/build/bin/server.js examples/too_many_cooks/bin/server.dart +dart run tools/build/add_preamble.dart \ + examples/too_many_cooks/build/bin/server.js \ + examples/too_many_cooks/build/bin/server_node.js \ + --shebang +``` + +#### 2b. Build and package the extension + +```bash +cd examples/too_many_cooks_vscode_extension +npm install +npm run compile +npx @vscode/vsce package +``` + +This creates a `.vsix` file in `examples/too_many_cooks_vscode_extension/`. + +#### 2c. Install the extension + +```bash +code --install-extension examples/too_many_cooks_vscode_extension/*.vsix +``` + +Or use the full build script that does steps 2a-2b: +```bash +bash examples/too_many_cooks_vscode_extension/build.sh +``` + +### Step 3: Configure Claude Code for this project + +The project's `.claude/settings.local.json` already has the required permissions: +- `Bash(dart pub get:*)` — dependency installation +- `mcp__too-many-cooks__register` — MCP agent registration +- `Bash(docker ps:*)` — Docker status checks + +Custom skills are in `.claude/skills/` — run `/help` to see them. + +### Multi-Agent Usage + +After setup, agents coordinate via the Too Many Cooks MCP server: +- **Lock files** before editing, unlock after +- **Check messages** regularly between agents +- **Update plans** so other agents can see your intent +- Keep your agent key — it's critical for authentication + +--- + +## Playwright Setup + +Sets up Playwright with Chromium for running the website's E2E test suite. + +### Steps + +1. **Install website npm dependencies** (includes `@playwright/test`): + ```bash + cd website && npm ci + ``` + +2. **Install Chromium browser and OS dependencies**: + ```bash + cd website && npx playwright install --with-deps chromium + ``` + This installs the Chromium binary plus required system libraries (libgbm, libasound, etc.). + +3. **Verify installation**: + ```bash + cd website && npx playwright --version + ``` + +### Notes + +- Only Chromium is needed — this project does not test against Firefox or WebKit. +- On the Dev Container (Ubuntu 24.04), `--with-deps` installs the OS packages automatically. +- On macOS, Playwright downloads its own Chromium binary — no Homebrew needed. +- Browser cache lives at `~/.cache/ms-playwright/`. Delete this to force a clean reinstall. +- The website tests are at `website/tests/` and configured in `website/playwright.config.js`. +- Base URL: `http://localhost:8080` (Eleventy dev server). + +### After installing + +Run the website tests: +```bash +cd website && npm test +``` + +Or with UI mode: +```bash +cd website && npm run test:ui +``` diff --git a/packages/dart_node_react/package-lock.json b/packages/dart_node_react/package-lock.json new file mode 100644 index 0000000..c56b568 --- /dev/null +++ b/packages/dart_node_react/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "dart_node_react", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/tools/test.sh b/tools/test.sh index dbb8346..e55a8b9 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -87,6 +87,19 @@ fi rm -rf "$LOGS_DIR" mkdir -p "$LOGS_DIR" +# Format seconds into human-readable time +format_time() { + local total_secs=$1 + local mins=$((total_secs / 60)) + local secs=$((total_secs % 60)) + + if [[ $mins -gt 0 ]]; then + echo "${mins}m ${secs}s" + else + echo "${secs}s" + fi +} + # Test a single package (runs in subshell) test_package() { local dir="$1" @@ -98,6 +111,9 @@ test_package() { cd "$full_path" + # Start timer + local start_time=$SECONDS + # Clear log file > "$log" @@ -106,7 +122,11 @@ test_package() { # Build first if needed if is_type "$dir" "$BUILD_FIRST" && [[ -f "build.sh" ]]; then - ./build.sh >> "$log" 2>&1 || { echo "⛔️ Failed $name (build)" ; return 1; } + ./build.sh >> "$log" 2>&1 || { + local elapsed=$((SECONDS - start_time)) + echo "⛔️ Failed $name (build) - $(format_time $elapsed)" + return 1 + } fi # Install npm deps if needed @@ -115,34 +135,58 @@ test_package() { local coverage="" if is_type "$dir" "$NPM_PACKAGES"; then - npm test >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } + npm test >> "$log" 2>&1 || { + local elapsed=$((SECONDS - start_time)) + echo "⛔️ Failed $name - $(format_time $elapsed)" + return 1 + } elif is_type "$dir" "$NODE_INTEROP_PACKAGES"; then # Node interop packages: use coverage CLI like NODE_PACKAGES - dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } + dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { + local elapsed=$((SECONDS - start_time)) + echo "⛔️ Failed $name - $(format_time $elapsed)" + return 1 + } coverage=$(calc_coverage "coverage/lcov.info") elif is_type "$dir" "$NODE_PACKAGES"; then - dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } + dart run "$COVERAGE_CLI" >> "$log" 2>&1 || { + local elapsed=$((SECONDS - start_time)) + echo "⛔️ Failed $name - $(format_time $elapsed)" + return 1 + } coverage=$(calc_coverage "coverage/lcov.info") elif is_type "$dir" "$BROWSER_PACKAGES"; then # Browser packages: run Chrome tests, check coverage if lcov.info exists - dart test -p chrome --reporter expanded --fail-fast >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } + dart test -p chrome --reporter expanded --fail-fast >> "$log" 2>&1 || { + local elapsed=$((SECONDS - start_time)) + echo "⛔️ Failed $name - $(format_time $elapsed)" + return 1 + } [[ -f "coverage/lcov.info" ]] && coverage=$(calc_coverage "coverage/lcov.info") else # Standard VM package with coverage - dart test --coverage=coverage --reporter expanded --fail-fast >> "$log" 2>&1 || { echo "⛔️ Failed $name"; return 1; } + dart test --coverage=coverage --reporter expanded --fail-fast >> "$log" 2>&1 || { + local elapsed=$((SECONDS - start_time)) + echo "⛔️ Failed $name - $(format_time $elapsed)" + return 1 + } dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --report-on=lib >> "$log" 2>&1 coverage=$(calc_coverage "coverage/lcov.info") fi + # Calculate elapsed time + local elapsed=$((SECONDS - start_time)) + local time_str=$(format_time $elapsed) + # Check coverage threshold if applicable if [[ -n "$coverage" ]]; then if [[ "$coverage" == "0" ]] || (( $(echo "$coverage < $MIN_COVERAGE" | bc -l) )); then - echo "⛔️ Failed $name (coverage ${coverage}% < ${MIN_COVERAGE}%)" + echo "⛔️ Failed $name (coverage ${coverage}% < ${MIN_COVERAGE}%) - $time_str" return 1 fi - echo "✅ Succeeded $name (${coverage}%)" + echo "✅ Succeeded $name (${coverage}%) - $time_str" else - echo "✅ Succeeded $name" + echo "✅ Succeeded $name - $time_str" fi return 0 } @@ -225,6 +269,8 @@ run_tier() { } # Main +TOTAL_START=$SECONDS + echo "Running ${#TIERS_TO_RUN[@]} tier(s) (MIN_COVERAGE=${MIN_COVERAGE}%)" echo "Excluded: $EXCLUDED" echo "Logs: $LOGS_DIR/" @@ -250,19 +296,27 @@ for tier_spec in "${TIERS_TO_RUN[@]}"; do echo "=== $TIER_LABEL: ${#tier_paths[@]} packages ===" fi + tier_start=$SECONDS if ! run_tier "${tier_paths[@]}"; then echo "" + local tier_elapsed=$((SECONDS - tier_start)) if [[ "$TIER_LABEL" == "ALL TIERS" ]]; then - echo "TIER $tier_num FAILED - stopping" + echo "TIER $tier_num FAILED - $(format_time $tier_elapsed)" else - echo "$TIER_LABEL FAILED" + echo "$TIER_LABEL FAILED - $(format_time $tier_elapsed)" fi exit 1 fi + local tier_elapsed=$((SECONDS - tier_start)) + + if [[ "$TIER_LABEL" == "ALL TIERS" ]]; then + echo "TIER $tier_num completed - $(format_time $tier_elapsed)" + fi echo "" ((tier_num++)) done -echo "All tests passed" +local total_elapsed=$((SECONDS - TOTAL_START)) +echo "All tests passed - $(format_time $total_elapsed)" exit 0 From 903aa801e4eb89f72f2a86d4f9e2362df8771880 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Wed, 28 Jan 2026 12:16:32 +1100 Subject: [PATCH 17/20] try fix --- tools/test.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tools/test.sh b/tools/test.sh index e55a8b9..20ae5e2 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -18,6 +18,37 @@ COVERAGE_CLI="$ROOT_DIR/packages/dart_node_coverage/bin/coverage.dart" # Minimum coverage threshold (can be overridden by MIN_COVERAGE env var) MIN_COVERAGE="${MIN_COVERAGE:-80}" +# Detect Chromium executable for browser tests (can be overridden by CHROME_EXECUTABLE env var) +if [[ -z "${CHROME_EXECUTABLE:-}" ]]; then + case "$(uname -s)" in + Darwin) + for candidate in \ + "/opt/homebrew/Caskroom/chromium/latest/chrome-mac/Chromium.app/Contents/MacOS/Chromium" \ + "/Applications/Chromium.app/Contents/MacOS/Chromium"; do + [[ -x "$candidate" ]] && { CHROME_EXECUTABLE="$candidate"; break; } + done + ;; + Linux) + for candidate in chromium chromium-browser; do + command -v "$candidate" >/dev/null 2>&1 && { CHROME_EXECUTABLE="$(command -v "$candidate")"; break; } + done + # Dev Container Chromium cache fallback + if [[ -z "${CHROME_EXECUTABLE:-}" ]]; then + for dir in /home/vscode/.cache/chromium-*/chrome-linux; do + [[ -x "$dir/chrome" ]] && { CHROME_EXECUTABLE="$dir/chrome"; break; } + done + fi + ;; + esac +fi + +if [[ -n "${CHROME_EXECUTABLE:-}" ]]; then + export CHROME_EXECUTABLE + echo "Chromium: $CHROME_EXECUTABLE" +else + echo "WARNING: No Chromium found — browser tests will fail" +fi + # Package type definitions NODE_PACKAGES="dart_node_core dart_node_express dart_node_ws dart_node_better_sqlite3" NODE_INTEROP_PACKAGES="dart_node_mcp dart_node_react_native too_many_cooks" From e9ff12ed6be2c8f7d1975d1e19baf8e96721c1fe Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:24:19 +1100 Subject: [PATCH 18/20] Fix coverage --- .../dart_node_coverage/lib/src/runtime.dart | 69 +- packages/dart_node_mcp/package-lock.json | 1128 +++++++++++++++++ packages/dart_node_mcp/package.json | 21 + packages/dart_node_mcp/test/runtime_test.dart | 277 ++++ tools/test.sh | 18 +- 5 files changed, 1506 insertions(+), 7 deletions(-) create mode 100644 packages/dart_node_mcp/package-lock.json create mode 100644 packages/dart_node_mcp/package.json create mode 100644 packages/dart_node_mcp/test/runtime_test.dart diff --git a/packages/dart_node_coverage/lib/src/runtime.dart b/packages/dart_node_coverage/lib/src/runtime.dart index 180e495..6bdbe22 100644 --- a/packages/dart_node_coverage/lib/src/runtime.dart +++ b/packages/dart_node_coverage/lib/src/runtime.dart @@ -16,6 +16,7 @@ extension type _NodeFS(JSObject _) implements JSObject { external void writeFileSync(JSString path, JSString data); external void mkdirSync(JSString path, JSObject? options); external bool existsSync(JSString path); + external JSString readFileSync(JSString path, [JSString? encoding]); } /// Extension type for Node.js path module @@ -92,6 +93,9 @@ String getCoverageJson() { } /// Write coverage data to a file (Node.js only) +/// +/// CRITICAL: Merges with existing coverage data instead of overwriting. +/// Multiple test files call this function and we accumulate coverage. void writeCoverageFile(String outputPath) { // Load Node.js modules final fsResult = _global.require.callAsFunction(null, 'fs'.toJS); @@ -105,7 +109,6 @@ void writeCoverageFile(String outputPath) { } // Type-checked casts: safe after isA() verification - // Extension type constructors require JSObject, casts are required for js_interop final fs = _NodeFS(fsResult as JSObject); final pathMod = _NodePath(pathResult as JSObject); @@ -116,7 +119,69 @@ void writeCoverageFile(String outputPath) { fs.mkdirSync(dir, options); } - // Write coverage data + // Get current coverage data + final currentData = _getCoverageData(); + + // If file exists, read and merge + if (fs.existsSync(outputPath.toJS)) { + try { + final existing = fs.readFileSync(outputPath.toJS, 'utf8'.toJS); + final json = (globalContext as JSObject)['JSON'] as JSObject; + final parse = json['parse'] as JSFunction; + final existingData = parse.callAsFunction(json, existing) as JSObject; + + // Merge existing into current + _mergeData(currentData, existingData); + } catch (_) { + // Corrupt/empty file - ignore and overwrite + } + } + + // Write merged data final jsonData = getCoverageJson(); fs.writeFileSync(outputPath.toJS, jsonData.toJS); } + +/// Merge existing coverage data into current data +void _mergeData(JSObject current, JSObject existing) { + final objClass = (globalContext as JSObject)['Object'] as JSObject; + final keys = objClass['keys'] as JSFunction; + final fileKeys = keys.callAsFunction(objClass, existing) as JSArray; + + final fileCount = (fileKeys.getProperty('length'.toJS) as JSNumber).toDartInt; + for (var i = 0; i < fileCount; i++) { + final fileKeyRaw = fileKeys.getProperty(i.toJS); + if (fileKeyRaw == null) continue; + final fileKey = fileKeyRaw as JSString; + final existingFileCov = existing.getProperty(fileKey) as JSObject; + + // Get or create file coverage + final hasFile = (current.hasProperty(fileKey) as JSBoolean).toDart; + final currentFileCov = hasFile + ? current.getProperty(fileKey) as JSObject + : JSObject(); + + if (!hasFile) { + current.setProperty(fileKey, currentFileCov); + } + + // Merge line counts + final lineKeys = keys.callAsFunction(objClass, existingFileCov) as JSArray; + final lineCount = (lineKeys.getProperty('length'.toJS) as JSNumber).toDartInt; + + for (var j = 0; j < lineCount; j++) { + final lineKeyRaw = lineKeys.getProperty(j.toJS); + if (lineKeyRaw == null) continue; + final lineKey = lineKeyRaw; + final existingCount = existingFileCov.getProperty(lineKey) as JSNumber; + final hasLine = (currentFileCov.hasProperty(lineKey) as JSBoolean).toDart; + final currentCount = hasLine + ? (currentFileCov.getProperty(lineKey) as JSNumber).toDartDouble + : 0.0; + + // Add counts together + final merged = currentCount + existingCount.toDartDouble; + currentFileCov.setProperty(lineKey, merged.toJS); + } + } +} diff --git a/packages/dart_node_mcp/package-lock.json b/packages/dart_node_mcp/package-lock.json new file mode 100644 index 0000000..81539a3 --- /dev/null +++ b/packages/dart_node_mcp/package-lock.json @@ -0,0 +1,1128 @@ +{ + "name": "dart_node_mcp", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "dart_node_mcp", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.25.3", + "zod": "^4.3.6" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.25.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.3.tgz", + "integrity": "sha512-vsAMBMERybvYgKbg/l4L1rhS7VXV1c0CtyJg72vwxONVX0l4ZfKVAnZEWTQixJGTzKnELjQ59e4NbdFDALRiAQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.0.1", + "express-rate-limit": "^7.5.0", + "jose": "^6.1.1", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", + "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/packages/dart_node_mcp/package.json b/packages/dart_node_mcp/package.json new file mode 100644 index 0000000..bf6aed4 --- /dev/null +++ b/packages/dart_node_mcp/package.json @@ -0,0 +1,21 @@ +{ + "name": "dart_node_mcp", + "version": "1.0.0", + "description": "MCP (Model Context Protocol) server bindings for Dart on Node.js. Build AI tool servers that can be used by Claude, GPT, and other AI assistants.", + "main": "index.js", + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.25.3", + "zod": "^4.3.6" + } +} diff --git a/packages/dart_node_mcp/test/runtime_test.dart b/packages/dart_node_mcp/test/runtime_test.dart new file mode 100644 index 0000000..c8110c0 --- /dev/null +++ b/packages/dart_node_mcp/test/runtime_test.dart @@ -0,0 +1,277 @@ +/// Runtime integration tests that exercise actual MCP SDK functionality. +@TestOn('node') +library; + +import 'package:dart_node_coverage/dart_node_coverage.dart'; +import 'package:dart_node_mcp/dart_node_mcp.dart'; +import 'package:nadz/nadz.dart'; +import 'package:test/test.dart'; + +void main() { + setUp(initCoverage); + tearDownAll(() => writeCoverageFile('coverage/coverage.json')); + + group('Runtime McpServer tests', () { + test('creates McpServer and registers tool', () { + const impl = (name: 'runtime-test', version: '1.0.0'); + final serverResult = McpServer.create(impl); + + // Server creation should succeed with MCP SDK installed + expect(serverResult.isSuccess, isTrue); + + if (serverResult.isSuccess) { + final server = (serverResult as Success).value; + + // Register a real tool + const config = ( + title: 'Echo Tool', + description: 'Echoes input', + inputSchema: null, + outputSchema: null, + annotations: null, + ); + + Future callback( + Map args, + ToolCallMeta? meta, + ) async => + ( + content: >[ + {'type': 'text', 'text': 'Echo: ${args['message']}'}, + ], + isError: false, + ); + + final toolResult = server.registerTool('echo', config, callback); + + // Tool registration should succeed + expect(toolResult.isSuccess, isTrue); + + if (toolResult.isSuccess) { + final tool = (toolResult as Success).value; + expect(tool.name, equals('echo')); + + // Test notifications (should not throw) + server.sendToolListChanged(); + } + } + }); + + test('creates McpServer and registers resource', () { + const impl = (name: 'resource-test', version: '1.0.0'); + final serverResult = McpServer.create(impl); + + expect(serverResult.isSuccess, isTrue); + + if (serverResult.isSuccess) { + final server = (serverResult as Success).value; + + const metadata = ( + description: 'Test resource', + mimeType: 'text/plain', + ); + + Future callback(String uri) async => ( + contents: >[ + { + 'type': 'resource', + 'uri': uri, + 'mimeType': 'text/plain', + 'text': 'Resource content', + }, + ], + ); + + final resourceResult = server.registerResource( + 'test-resource', + 'file:///test.txt', + metadata, + callback, + ); + + expect(resourceResult.isSuccess, isTrue); + + if (resourceResult.isSuccess) { + final resource = (resourceResult as Success).value; + expect(resource.name, equals('test-resource')); + expect(resource.uri, equals('file:///test.txt')); + + server.sendResourceListChanged(); + } + } + }); + + test('creates McpServer and registers prompt', () { + const impl = (name: 'prompt-test', version: '1.0.0'); + final serverResult = McpServer.create(impl); + + expect(serverResult.isSuccess, isTrue); + + if (serverResult.isSuccess) { + final server = (serverResult as Success).value; + + const config = ( + title: 'Test Prompt', + description: 'A test prompt', + argsSchema: null, + ); + + Future callback(Map args) async => ( + description: 'Test prompt result', + messages: [ + (role: 'assistant', content: {'type': 'text', 'text': 'Prompt response'}), + ], + ); + + final promptResult = server.registerPrompt('test-prompt', config, callback); + + expect(promptResult.isSuccess, isTrue); + + if (promptResult.isSuccess) { + final prompt = (promptResult as Success).value; + expect(prompt.name, equals('test-prompt')); + + server.sendPromptListChanged(); + } + } + }); + + test('creates McpServer with capabilities', () { + const impl = (name: 'capabilities-test', version: '1.0.0'); + const options = ( + capabilities: ( + tools: (listChanged: true), + resources: (subscribe: true, listChanged: true), + prompts: (listChanged: false), + logging: (enabled: true), + ), + instructions: 'Test server', + ); + + final result = McpServer.create(impl, options: options); + + expect(result.isSuccess, isTrue); + + if (result.isSuccess) { + final server = (result as Success).value; + + // Can access underlying server + expect(server.server, isNotNull); + + // isConnected should work + expect(server.isConnected(), isFalse); + } + }); + + test('creates stdio transport', () { + final result = createStdioServerTransport(); + + // Should create successfully + expect(result.isSuccess, isTrue); + + if (result.isSuccess) { + final transport = (result as Success).value; + expect(transport, isNotNull); + } + }); + + test('creates low-level Server', () { + const impl = (name: 'low-level-test', version: '1.0.0'); + final result = createServer(impl); + + expect(result.isSuccess, isTrue); + + if (result.isSuccess) { + final server = (result as Success).value; + expect(server, isNotNull); + } + }); + + test('registers multiple tools and resources on same server', () { + const impl = (name: 'multi-register-test', version: '1.0.0'); + final serverResult = McpServer.create(impl); + + expect(serverResult.isSuccess, isTrue); + + if (serverResult.isSuccess) { + final server = (serverResult as Success).value; + + // Register first tool + const config1 = ( + title: null, + description: 'Tool 1', + inputSchema: null, + outputSchema: null, + annotations: null, + ); + + Future callback1( + Map args, + ToolCallMeta? meta, + ) async => + ( + content: >[ + {'type': 'text', 'text': 'Tool 1'}, + ], + isError: null, + ); + + final tool1 = server.registerTool('tool1', config1, callback1); + expect(tool1.isSuccess, isTrue); + + // Register second tool + const config2 = ( + title: null, + description: 'Tool 2', + inputSchema: null, + outputSchema: null, + annotations: null, + ); + + Future callback2( + Map args, + ToolCallMeta? meta, + ) async => + ( + content: >[ + {'type': 'text', 'text': 'Tool 2'}, + ], + isError: null, + ); + + final tool2 = server.registerTool('tool2', config2, callback2); + expect(tool2.isSuccess, isTrue); + + // Register a resource + const metadata = ( + description: 'Resource 1', + mimeType: 'application/json', + ); + + Future resourceCallback(String uri) async => ( + contents: >[ + { + 'type': 'resource', + 'uri': uri, + 'mimeType': 'application/json', + 'text': '{}', + }, + ], + ); + + final resource1 = server.registerResource( + 'resource1', + 'file:///resource1.json', + metadata, + resourceCallback, + ); + expect(resource1.isSuccess, isTrue); + + // All notifications should work + server.sendToolListChanged(); + server.sendResourceListChanged(); + server.sendPromptListChanged(); + } + }); + }); +} diff --git a/tools/test.sh b/tools/test.sh index 20ae5e2..24947dc 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -29,10 +29,18 @@ if [[ -z "${CHROME_EXECUTABLE:-}" ]]; then done ;; Linux) + # Try system chromium first for candidate in chromium chromium-browser; do - command -v "$candidate" >/dev/null 2>&1 && { CHROME_EXECUTABLE="$(command -v "$candidate")"; break; } + if command -v "$candidate" >/dev/null 2>&1; then + candidate_path="$(command -v "$candidate")" + # Test if it actually runs (not just a snap wrapper) + if "$candidate_path" --version >/dev/null 2>&1; then + CHROME_EXECUTABLE="$candidate_path" + break + fi + fi done - # Dev Container Chromium cache fallback + # Fallback to dev container cache if system chromium doesn't work if [[ -z "${CHROME_EXECUTABLE:-}" ]]; then for dir in /home/vscode/.cache/chromium-*/chrome-linux; do [[ -x "$dir/chrome" ]] && { CHROME_EXECUTABLE="$dir/chrome"; break; } @@ -330,7 +338,7 @@ for tier_spec in "${TIERS_TO_RUN[@]}"; do tier_start=$SECONDS if ! run_tier "${tier_paths[@]}"; then echo "" - local tier_elapsed=$((SECONDS - tier_start)) + tier_elapsed=$((SECONDS - tier_start)) if [[ "$TIER_LABEL" == "ALL TIERS" ]]; then echo "TIER $tier_num FAILED - $(format_time $tier_elapsed)" else @@ -338,7 +346,7 @@ for tier_spec in "${TIERS_TO_RUN[@]}"; do fi exit 1 fi - local tier_elapsed=$((SECONDS - tier_start)) + tier_elapsed=$((SECONDS - tier_start)) if [[ "$TIER_LABEL" == "ALL TIERS" ]]; then echo "TIER $tier_num completed - $(format_time $tier_elapsed)" @@ -348,6 +356,6 @@ for tier_spec in "${TIERS_TO_RUN[@]}"; do ((tier_num++)) done -local total_elapsed=$((SECONDS - TOTAL_START)) +total_elapsed=$((SECONDS - TOTAL_START)) echo "All tests passed - $(format_time $total_elapsed)" exit 0 From 8f26a5ad9dc5bb2fb4afaad8ef8c3f720964a104 Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:27:22 +1100 Subject: [PATCH 19/20] Chrome settings --- examples/reflux_demo/web_counter/dart_test.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/reflux_demo/web_counter/dart_test.yaml b/examples/reflux_demo/web_counter/dart_test.yaml index fdaf610..017ca30 100644 --- a/examples/reflux_demo/web_counter/dart_test.yaml +++ b/examples/reflux_demo/web_counter/dart_test.yaml @@ -1,3 +1,8 @@ platforms: [chrome] custom_html_template_path: test/test_template.html + +override_platforms: + chrome: + settings: + arguments: --no-sandbox From b7d145309ed63a7dec64cf83b5923fdf03e3566f Mon Sep 17 00:00:00 2001 From: Christian Findlay <16697547+MelbourneDeveloper@users.noreply.github.com> Date: Thu, 29 Jan 2026 20:58:24 +1100 Subject: [PATCH 20/20] fix tests --- .../dart_node_coverage/lib/src/runtime.dart | 3 +- packages/dart_node_mcp/test/runtime_test.dart | 64 ++++++++++--------- tools/test.sh | 9 +++ 3 files changed, 45 insertions(+), 31 deletions(-) diff --git a/packages/dart_node_coverage/lib/src/runtime.dart b/packages/dart_node_coverage/lib/src/runtime.dart index 6bdbe22..343054e 100644 --- a/packages/dart_node_coverage/lib/src/runtime.dart +++ b/packages/dart_node_coverage/lib/src/runtime.dart @@ -167,7 +167,8 @@ void _mergeData(JSObject current, JSObject existing) { // Merge line counts final lineKeys = keys.callAsFunction(objClass, existingFileCov) as JSArray; - final lineCount = (lineKeys.getProperty('length'.toJS) as JSNumber).toDartInt; + final lineCount = + (lineKeys.getProperty('length'.toJS) as JSNumber).toDartInt; for (var j = 0; j < lineCount; j++) { final lineKeyRaw = lineKeys.getProperty(j.toJS); diff --git a/packages/dart_node_mcp/test/runtime_test.dart b/packages/dart_node_mcp/test/runtime_test.dart index c8110c0..6ef7f08 100644 --- a/packages/dart_node_mcp/test/runtime_test.dart +++ b/packages/dart_node_mcp/test/runtime_test.dart @@ -34,13 +34,12 @@ void main() { Future callback( Map args, ToolCallMeta? meta, - ) async => - ( - content: >[ - {'type': 'text', 'text': 'Echo: ${args['message']}'}, - ], - isError: false, - ); + ) async => ( + content: >[ + {'type': 'text', 'text': 'Echo: ${args['message']}'}, + ], + isError: false, + ); final toolResult = server.registerTool('echo', config, callback); @@ -66,10 +65,7 @@ void main() { if (serverResult.isSuccess) { final server = (serverResult as Success).value; - const metadata = ( - description: 'Test resource', - mimeType: 'text/plain', - ); + const metadata = (description: 'Test resource', mimeType: 'text/plain'); Future callback(String uri) async => ( contents: >[ @@ -92,7 +88,8 @@ void main() { expect(resourceResult.isSuccess, isTrue); if (resourceResult.isSuccess) { - final resource = (resourceResult as Success).value; + final resource = + (resourceResult as Success).value; expect(resource.name, equals('test-resource')); expect(resource.uri, equals('file:///test.txt')); @@ -119,16 +116,24 @@ void main() { Future callback(Map args) async => ( description: 'Test prompt result', messages: [ - (role: 'assistant', content: {'type': 'text', 'text': 'Prompt response'}), + ( + role: 'assistant', + content: {'type': 'text', 'text': 'Prompt response'}, + ), ], ); - final promptResult = server.registerPrompt('test-prompt', config, callback); + final promptResult = server.registerPrompt( + 'test-prompt', + config, + callback, + ); expect(promptResult.isSuccess, isTrue); if (promptResult.isSuccess) { - final prompt = (promptResult as Success).value; + final prompt = + (promptResult as Success).value; expect(prompt.name, equals('test-prompt')); server.sendPromptListChanged(); @@ -170,7 +175,8 @@ void main() { expect(result.isSuccess, isTrue); if (result.isSuccess) { - final transport = (result as Success).value; + final transport = + (result as Success).value; expect(transport, isNotNull); } }); @@ -208,13 +214,12 @@ void main() { Future callback1( Map args, ToolCallMeta? meta, - ) async => - ( - content: >[ - {'type': 'text', 'text': 'Tool 1'}, - ], - isError: null, - ); + ) async => ( + content: >[ + {'type': 'text', 'text': 'Tool 1'}, + ], + isError: null, + ); final tool1 = server.registerTool('tool1', config1, callback1); expect(tool1.isSuccess, isTrue); @@ -231,13 +236,12 @@ void main() { Future callback2( Map args, ToolCallMeta? meta, - ) async => - ( - content: >[ - {'type': 'text', 'text': 'Tool 2'}, - ], - isError: null, - ); + ) async => ( + content: >[ + {'type': 'text', 'text': 'Tool 2'}, + ], + isError: null, + ); final tool2 = server.registerTool('tool2', config2, callback2); expect(tool2.isSuccess, isTrue); diff --git a/tools/test.sh b/tools/test.sh index 24947dc..1a592dc 100755 --- a/tools/test.sh +++ b/tools/test.sh @@ -126,6 +126,15 @@ fi rm -rf "$LOGS_DIR" mkdir -p "$LOGS_DIR" +# Ensure coverage CLI dependencies are resolved for this environment +# (paths differ between Mac and Linux container) +(cd "$ROOT_DIR/packages/dart_node_coverage" && dart pub get --offline 2>/dev/null || dart pub get) >/dev/null + +# Rebuild native npm modules if needed (architecture differs between Mac and Linux) +if [[ -d "$ROOT_DIR/packages/dart_node_better_sqlite3/node_modules/better-sqlite3" ]]; then + (cd "$ROOT_DIR/packages/dart_node_better_sqlite3" && npm rebuild better-sqlite3 2>/dev/null) >/dev/null +fi + # Format seconds into human-readable time format_time() { local total_secs=$1