From 96f1758d4f58dd33e2fb187da98a95f01e24c13f Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Mon, 18 Aug 2025 20:14:41 +0100 Subject: [PATCH 01/18] docs: add phase plan outlining 12-week implementation timeline --- docs/phase_plan.md | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 docs/phase_plan.md diff --git a/docs/phase_plan.md b/docs/phase_plan.md new file mode 100644 index 0000000..bbeca70 --- /dev/null +++ b/docs/phase_plan.md @@ -0,0 +1,89 @@ +# ToolVault Phase Plan + +## Overview +This document outlines the high-level phases for implementing ToolVault, emphasizing early feedback through mock JavaScript infrastructure before full backend development. + +## Phase 1: Mock JavaScript Foundation (Weeks 1-2) +**Goal:** Rapidly prototype UI with functional JavaScript tools for immediate feedback + +- Set up React/TypeScript SPA with Vite +- Implement metadata-driven UI from `index.json` +- Create tool discovery and browsing interface +- Build dynamic input forms and output renderers +- Integrate existing JavaScript mock tools +- Deploy to GitHub Pages for stakeholder review + +## Phase 2: Enhanced UI & Tool Execution (Weeks 3-4) +**Goal:** Complete UI features with full mock tool functionality + +- Add LeafletJS for spatial visualization +- Implement execution history tracking +- Create pipeline capture and replay +- Add provenance display (PROV format preview) +- Implement multi-format output viewers +- Enhance search with fuzzy matching + + +## Phase 3: Platform Integration (Weeks 9-10) +**Goal:** Enable multi-platform embedding + +- Develop Debrief plugin adapter +- Create VS Code extension prototype +- Implement MCP (Model Context Protocol) support +- Add REST API documentation +- Create embedding examples +- Platform-specific testing + +## Phase 4: Backend Infrastructure (Weeks 5-6) +**Goal:** Establish Python backend while maintaining JS mock tools + +- Set up FastAPI backend structure +- Create REST API endpoints for tool discovery +- Implement dual runtime support (JS + Python) +- Add tool execution service layer +- Integrate with existing JavaScript tools +- Maintain GitHub Pages deployment + +## Phase 5: Python Tool Integration (Weeks 7-8) +**Goal:** Transition to production tool execution + +- Port key JavaScript tools to Python +- Implement Python tool execution engine +- Add Web Worker isolation for JS tools +- Create tool validation framework +- Implement error handling and recovery +- Performance optimization + +## Phase 6: Production Readiness (Weeks 11-12) +**Goal:** Prepare for deployment and scaling + +- Implement caching strategy +- Add audit trail functionality +- Complete WCAG 2.1 AA compliance +- Performance testing and optimization +- Security hardening +- Documentation and training materials + +## Key Principles + +1. **Early Feedback**: Deploy working UI with mock tools by end of Week 2 +2. **Incremental Value**: Each phase delivers usable functionality +3. **Dual Runtime**: Support both JavaScript and Python tools throughout +4. **Metadata-Driven**: All UI generated from `index.json` specifications +5. **Platform Agnostic**: REST API design supports all integration targets + +## Success Metrics + +- Week 2: Stakeholders can browse and execute mock tools +- Week 4: Complete UI with pipeline functionality available +- Week 6: Backend supports both JS and Python execution +- Week 8: Production tools running with full provenance +- Week 10: Successfully embedded in at least one platform +- Week 12: Production-ready system meeting all requirements + +## Risk Mitigation + +- **Technical Risk**: Use proven technologies (React, FastAPI, LeafletJS) +- **Adoption Risk**: Early demos with mock tools to gather feedback +- **Integration Risk**: REST API approach simplifies platform integration +- **Performance Risk**: JavaScript mock tools validate UI performance early \ No newline at end of file From 228a393391c7a9783a9766affdf9e90bcfffbaeb Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 14:29:26 +0100 Subject: [PATCH 02/18] docs: add Phase 0 for JavaScript toolbox development and reorder implementation phases --- docs/phase_plan.md | 51 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/docs/phase_plan.md b/docs/phase_plan.md index bbeca70..0286dbf 100644 --- a/docs/phase_plan.md +++ b/docs/phase_plan.md @@ -3,14 +3,35 @@ ## Overview This document outlines the high-level phases for implementing ToolVault, emphasizing early feedback through mock JavaScript infrastructure before full backend development. -## Phase 1: Mock JavaScript Foundation (Weeks 1-2) +## Phase 0: JavaScript Toolbox Repository (Week 1) +**Goal:** Create standalone JavaScript toolbox with comprehensive unit tests as reference implementation + +- Set up `/examples/javascript-bundle/` directory structure with Jest testing framework +- Implement 10-15 JavaScript tools for GeoJSON processing and spatial analysis following ADR-013: + - **Transform tools**: Translate, flip horizontal/vertical + - **Analysis tools**: Speed series, direction series calculations + - **Statistics tools**: Average speed, speed histogram + - **Processing tools**: Polyline smoothing algorithms + - **I/O tools**: REP file import/export, CSV export +- Create comprehensive unit tests for each tool implementation: + - Test geometric transformations with known coordinate sets + - Verify temporal calculations with sample GPS tracks + - Validate statistical computations against expected results + - Test file format conversions with reference data +- Establish IIFE pattern for browser-compatible tool execution +- Create `index.json` with runtime field and complete tool metadata +- Add sample GPS track data and test fixtures for realistic testing +- Document tool specifications and testing approach +- Ensure 100% test coverage for all tool methods + +## Phase 1: Mock JavaScript Foundation (Week 2) **Goal:** Rapidly prototype UI with functional JavaScript tools for immediate feedback - Set up React/TypeScript SPA with Vite - Implement metadata-driven UI from `index.json` - Create tool discovery and browsing interface - Build dynamic input forms and output renderers -- Integrate existing JavaScript mock tools +- Integrate JavaScript toolbox from Phase 0 - Deploy to GitHub Pages for stakeholder review ## Phase 2: Enhanced UI & Tool Execution (Weeks 3-4) @@ -23,18 +44,7 @@ This document outlines the high-level phases for implementing ToolVault, emphasi - Implement multi-format output viewers - Enhance search with fuzzy matching - -## Phase 3: Platform Integration (Weeks 9-10) -**Goal:** Enable multi-platform embedding - -- Develop Debrief plugin adapter -- Create VS Code extension prototype -- Implement MCP (Model Context Protocol) support -- Add REST API documentation -- Create embedding examples -- Platform-specific testing - -## Phase 4: Backend Infrastructure (Weeks 5-6) +## Phase 3: Backend Infrastructure (Weeks 5-6) **Goal:** Establish Python backend while maintaining JS mock tools - Set up FastAPI backend structure @@ -44,7 +54,7 @@ This document outlines the high-level phases for implementing ToolVault, emphasi - Integrate with existing JavaScript tools - Maintain GitHub Pages deployment -## Phase 5: Python Tool Integration (Weeks 7-8) +## Phase 4: Python Tool Integration (Weeks 7-8) **Goal:** Transition to production tool execution - Port key JavaScript tools to Python @@ -54,6 +64,16 @@ This document outlines the high-level phases for implementing ToolVault, emphasi - Implement error handling and recovery - Performance optimization +## Phase 5: Platform Integration (Weeks 9-10) +**Goal:** Enable multi-platform embedding + +- Develop Debrief plugin adapter +- Create VS Code extension prototype +- Implement MCP (Model Context Protocol) support +- Add REST API documentation +- Create embedding examples +- Platform-specific testing + ## Phase 6: Production Readiness (Weeks 11-12) **Goal:** Prepare for deployment and scaling @@ -74,6 +94,7 @@ This document outlines the high-level phases for implementing ToolVault, emphasi ## Success Metrics +- Week 1: JavaScript toolbox with 100% test coverage and comprehensive tool suite - Week 2: Stakeholders can browse and execute mock tools - Week 4: Complete UI with pipeline functionality available - Week 6: Backend supports both JS and Python execution From e8a7d3c02f11a286dba5ad7e5947996e0987161f Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 14:36:03 +0100 Subject: [PATCH 03/18] docs: reorganize phase plan to prioritize MCP and VS Code integration --- docs/phase_plan.md | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/docs/phase_plan.md b/docs/phase_plan.md index 0286dbf..16c1927 100644 --- a/docs/phase_plan.md +++ b/docs/phase_plan.md @@ -28,6 +28,7 @@ This document outlines the high-level phases for implementing ToolVault, emphasi **Goal:** Rapidly prototype UI with functional JavaScript tools for immediate feedback - Set up React/TypeScript SPA with Vite +- Load standard `bundle` from Phase 0 - Implement metadata-driven UI from `index.json` - Create tool discovery and browsing interface - Build dynamic input forms and output renderers @@ -38,13 +39,17 @@ This document outlines the high-level phases for implementing ToolVault, emphasi **Goal:** Complete UI features with full mock tool functionality - Add LeafletJS for spatial visualization -- Implement execution history tracking -- Create pipeline capture and replay -- Add provenance display (PROV format preview) -- Implement multi-format output viewers +- Implement multi-format input and output viewers - Enhance search with fuzzy matching -## Phase 3: Backend Infrastructure (Weeks 5-6) +## Phase 3: Create deployable instance + +- Implement MCP (Model Context Protocol) support +- Add REST API documentation +- Create deployable instance of ToolVault, that vs-code "Command Toolbox" can run against +- Let instance mature as reqired during vs-code development. + +## Phase 4: Backend Infrastructure (Weeks 5-6) **Goal:** Establish Python backend while maintaining JS mock tools - Set up FastAPI backend structure @@ -54,7 +59,7 @@ This document outlines the high-level phases for implementing ToolVault, emphasi - Integrate with existing JavaScript tools - Maintain GitHub Pages deployment -## Phase 4: Python Tool Integration (Weeks 7-8) +## Phase 5: Python Tool Integration (Weeks 7-8) **Goal:** Transition to production tool execution - Port key JavaScript tools to Python @@ -64,16 +69,6 @@ This document outlines the high-level phases for implementing ToolVault, emphasi - Implement error handling and recovery - Performance optimization -## Phase 5: Platform Integration (Weeks 9-10) -**Goal:** Enable multi-platform embedding - -- Develop Debrief plugin adapter -- Create VS Code extension prototype -- Implement MCP (Model Context Protocol) support -- Add REST API documentation -- Create embedding examples -- Platform-specific testing - ## Phase 6: Production Readiness (Weeks 11-12) **Goal:** Prepare for deployment and scaling From c60f125478164d13ea927bc99cc766e4e9226990 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 14:44:28 +0100 Subject: [PATCH 04/18] docs: create implementation plan with 4 phases and detailed task breakdown --- Implementation_Plan.md | 407 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 Implementation_Plan.md diff --git a/Implementation_Plan.md b/Implementation_Plan.md new file mode 100644 index 0000000..3c86984 --- /dev/null +++ b/Implementation_Plan.md @@ -0,0 +1,407 @@ +# Implementation Plan + +Project Goal: Create comprehensive JavaScript toolbox repository with full test coverage, then develop ToolVault's frontend interface and deployable service instance for stakeholder validation and vs-code integration. + +## Phase 0: JavaScript Toolbox Repository - Agent Group Alpha (Agent_JS_Dev, Agent_Test_Specialist) + +### Task 0.1 - Agent_JS_Dev: Project Structure and Jest Setup +Objective: Establish the JavaScript toolbox repository with comprehensive testing framework. + +1. Create project directory structure. + - Set up `/examples/javascript-bundle/` directory + - Create subdirectories: `/tools/`, `/data/`, `/tests/`, `/schemas/` + - Initialize `package.json` with Jest testing configuration + - Configure npm scripts: `test`, `test:watch`, `test:coverage` +2. Install and configure Jest testing framework. + - Install Jest with ES6 module support + - Configure Jest for browser-like environment (jsdom) + - Set up code coverage reporting with 100% target + - Create shared test utilities in `/tests/helpers.js` +3. Create sample data and test fixtures. + - Generate `sample-track.geojson` with GPS track containing timestamps + - Create `sample-features.geojson` with mixed FeatureCollection + - Add test data for different geometric types (Point, LineString, Polygon) + - Create reference outputs for validation testing + +### Task 0.2 - Agent_JS_Dev: Transform Tools Implementation +Objective: Implement geometric transformation tools with IIFE pattern following ADR-013 specification. + +1. Implement Translate Features tool. + - Create `tools/transform/translate.js` using IIFE pattern + - Function signature: `translateFeatures(input, params)` where params = {direction, distance} + - Apply geometric translation to all features in GeoJSON FeatureCollection + - Handle coordinate system transformations and unit conversions + - Guidance: Use spherical geometry for accurate distance calculations +2. Implement Flip Horizontal tool. + - Create `tools/transform/flip.js` with horizontal flip functionality + - Support flipping across longitude axis with proper coordinate handling + - Preserve feature properties and maintain GeoJSON structure + - Handle edge cases like features crossing antimeridian +3. Implement Flip Vertical tool. + - Extend flip.js with vertical flip capability across latitude axis + - Support axis parameter: "longitude" or "latitude" + - Ensure proper coordinate validation and bounds checking + - Maintain spatial relationships between features + +### Task 0.3 - Agent_Test_Specialist: Transform Tools Unit Testing +Objective: Create comprehensive unit tests for all transformation tools with 100% coverage. + +1. Test Translate Features functionality. + - Test translation with known coordinate sets and expected outputs + - Verify distance and direction calculations with reference data + - Test edge cases: zero distance, invalid directions, empty inputs + - Validate GeoJSON structure preservation and property retention +2. Test Flip operations. + - Test horizontal flip with coordinates spanning different longitudes + - Test vertical flip with various latitude ranges + - Verify axis parameter validation and error handling + - Test with different geometry types and complex features +3. Integration testing setup. + - Test tool loading through IIFE pattern + - Verify `window.ToolVault.tools` namespace availability + - Test tools with realistic sample data from `/data/` directory + - Create test suites for browser compatibility + +### Task 0.4 - Agent_JS_Dev: Analysis Tools Implementation +Objective: Implement temporal analysis tools for GPS tracks and time series calculations. + +1. Implement Calculate Speed Series tool. + - Create `tools/analysis/speed-series.js` for temporal speed calculations + - Parse timestamps from GeoJSON LineString coordinates or properties + - Calculate speed between consecutive points with time_unit parameter + - Return JSON time series with timestamp/speed pairs + - Guidance: Handle different timestamp formats (ISO 8601, Unix epoch) +2. Implement Calculate Direction Series tool. + - Create `tools/analysis/direction-series.js` for bearing calculations + - Calculate bearing/heading between consecutive GPS points + - Support smoothing parameter with configurable window_size + - Return JSON time series with timestamp/direction pairs + - Apply smoothing algorithms (moving average) when requested +3. Handle temporal data edge cases. + - Validate timestamp presence and format in input data + - Handle irregular time intervals and missing data points + - Implement proper error handling for invalid temporal sequences + - Support different coordinate systems and projection handling + +### Task 0.5 - Agent_Test_Specialist: Analysis Tools Unit Testing +Objective: Verify temporal calculations against known reference data with comprehensive test coverage. + +1. Test Speed Series calculations. + - Create reference GPS track with known speeds for validation + - Test with different time units (seconds, minutes, hours) + - Verify speed calculations using haversine formula references + - Test edge cases: stationary points, high-speed segments, time gaps +2. Test Direction Series calculations. + - Validate bearing calculations with known coordinate pairs + - Test smoothing algorithms with synthetic data + - Verify direction continuity and angle normalization + - Test with different window_size values and smoothing effects +3. Temporal data validation testing. + - Test with various timestamp formats and time zones + - Verify error handling for missing or invalid timestamps + - Test performance with large datasets (1000+ points) + - Validate memory usage and execution time constraints + +### Task 0.6 - Agent_JS_Dev: Statistics and Processing Tools +Objective: Complete the tool suite with statistical analysis and data processing capabilities. + +1. Implement Statistical tools. + - Create `tools/statistics/average-speed.js` for speed averaging with time_unit support + - Create `tools/statistics/speed-histogram.js` with configurable bins and interval_minutes + - Calculate statistical measures: mean, median, standard deviation + - Return structured JSON with statistical metadata +2. Implement Processing tools. + - Create `tools/processing/smooth-polyline.js` with multiple algorithms + - Support moving_average and gaussian smoothing with window_size parameter + - Preserve LineString structure while smoothing coordinate sequences + - Maintain topology and avoid self-intersections +3. Implement I/O tools. + - Create `tools/io/import-rep.js` for REP file format parsing + - Create `tools/io/export-rep.js` and `tools/io/export-csv.js` for data export + - Handle different encoding formats and precision settings + - Support coordinate_format options for CSV export (separate/wkt) + +### Task 0.7 - Agent_Test_Specialist: Complete Test Suite and Coverage +Objective: Ensure 100% test coverage across all tools with comprehensive validation. + +1. Test Statistical tools. + - Validate statistical calculations against reference implementations + - Test histogram generation with different bin sizes and intervals + - Verify edge cases: empty data, single points, outliers + - Test numerical precision and rounding behavior +2. Test Processing and I/O tools. + - Validate smoothing algorithms with known input/output pairs + - Test REP file format parsing with sample files + - Verify CSV/REP export format compliance + - Test file handling with different encodings and precisions +3. Complete coverage and documentation. + - Achieve 100% code coverage across all tool implementations + - Create comprehensive `index.json` with runtime field and complete metadata + - Document tool specifications and testing approach + - Generate coverage reports and performance benchmarks + +## Phase 1: Mock JavaScript Foundation - Agent Group Beta (Agent_Frontend_Lead) + +### Task 1.1 - Agent_Frontend_Lead: React/TypeScript SPA Setup +Objective: Establish the core frontend application infrastructure using modern web development tools. + +1. Initialize React/TypeScript project with Vite. + - Run `npm create vite@latest toolvault-frontend -- --template react-ts` + - Configure TypeScript with strict mode enabled + - Set up project directory structure: `/src/components`, `/src/services`, `/src/types` + - Configure Vite for development and production builds +2. Install and configure essential dependencies. + - Add React Router for client-side routing + - Install Axios for HTTP requests + - Add utility libraries: lodash, date-fns + - Configure ESLint and Prettier for code consistency +3. Set up development environment. + - Configure VS Code settings and extensions recommendations + - Set up npm scripts for dev, build, test, and lint + - Create basic folder structure and initial components + +### Task 1.2 - Agent_Frontend_Lead: Load Standard Bundle from Phase 0 +Objective: Integrate the JavaScript toolbox created in Phase 0 as the standard tool bundle. + +1. Create bundle loading service. + - Reference `/examples/javascript-bundle/index.json` as primary bundle source + - Implement service to fetch and parse tool metadata from Phase 0 + - Create TypeScript interfaces matching Phase 0 tool definitions + - Handle loading errors with fallback mechanisms +2. Implement tool registry system. + - Create in-memory registry for tools from Phase 0 bundle + - Support filtering by category, runtime type, and search terms + - Implement tool metadata caching for performance + - Add tool availability status tracking +3. Load Phase 0 JavaScript tools. + - Dynamically load tool scripts using IIFE pattern from Phase 0 + - Verify tool function availability in `window.ToolVault.tools` + - Handle script loading errors gracefully + - Test integration with all 10-15 tools from Phase 0 + +### Task 1.3 - Agent_Frontend_Lead: Metadata-Driven UI from index.json +Objective: Create dynamic UI components that generate interfaces directly from Phase 0 tool metadata. + +1. Parse tool metadata from Phase 0 index.json. + - Load and validate index.json structure with runtime field + - Create TypeScript interfaces for tool definitions and parameters + - Support parameter constraints and validation rules + - Handle metadata parsing errors and validation +2. Implement dynamic form generation. + - Generate forms dynamically based on Phase 0 parameter schemas + - Support all parameter types used in Phase 0 tools (number, string, boolean, enum) + - Implement form state management with validation + - Add parameter help text and validation error display +3. Create output rendering system. + - Support output formats from Phase 0: GeoJSON, JSON, Files + - Implement tabbed output viewer (Raw, Preview, Download) + - Add syntax highlighting for JSON/GeoJSON outputs + - Handle file output generation and download triggers + +### Task 1.4 - Agent_Frontend_Lead: Tool Discovery and Browsing Interface +Objective: Build intuitive interface for browsing and discovering Phase 0 JavaScript tools. + +1. Implement tool browser component. + - Create grid/list view showing Phase 0 tools with metadata cards + - Display tool categories: Transform, Analysis, Statistics, Processing, I/O + - Show tool name, description, parameter count, and runtime type + - Implement responsive design for different screen sizes +2. Add search and filtering capabilities. + - Implement text search across Phase 0 tool names and descriptions + - Add category-based filtering matching Phase 0 tool organization + - Support runtime type filtering (JavaScript initially) + - Create clear/reset filters functionality +3. Design tool detail view. + - Show complete tool information from Phase 0 metadata + - Display input/output specifications and parameter details + - Add "Try Tool" button to navigate to execution interface + - Include tool metadata like version and runtime requirements + +### Task 1.5 - Agent_Frontend_Lead: Dynamic Input Forms and Output Renderers +Objective: Create the execution interface that works with Phase 0 tool specifications. + +1. Build tool execution page. + - Display tool information and dynamically generated parameter forms + - Use Phase 0 sample data for quick testing (sample-track.geojson, etc.) + - Implement real-time parameter validation based on Phase 0 schemas + - Create execute button with loading states and progress indication +2. Implement JavaScript tool execution engine. + - Execute Phase 0 tools using `window.ToolVault.tools[toolId](input, params)` + - Handle synchronous tool execution with proper error catching + - Support different input types including Phase 0 sample data + - Implement execution timeout and error recovery +3. Create results display system. + - Multi-tab output viewer for Phase 0 tool outputs + - Support GeoJSON, JSON, and file download outputs from Phase 0 + - Prepare for LeafletJS integration for GeoJSON visualization + - Add execution history tracking (local storage initially) + +### Task 1.6 - Agent_Frontend_Lead: GitHub Pages Deployment +Objective: Deploy the working application with Phase 0 tools for stakeholder review. + +1. Configure deployment pipeline. + - Set up GitHub Actions workflow for automated builds + - Configure Vite build to include Phase 0 bundle assets + - Handle client-side routing with 404.html fallback + - Optimize bundle size and implement code splitting +2. Deploy to GitHub Pages. + - Configure repository settings for GitHub Pages + - Ensure Phase 0 JavaScript bundle is accessible in deployment + - Test all Phase 0 tools work correctly in production environment + - Verify tool execution, parameter handling, and output rendering +3. Create deployment documentation. + - Document deployment process including Phase 0 bundle integration + - Create user guide for stakeholder testing of Phase 0 tools + - Set up feedback collection mechanism (GitHub issues template) + - Prepare demo script highlighting Phase 0 tool capabilities + +## Phase 2: Enhanced UI & Tool Execution - Agent Group Gamma (Agent_UI_Specialist, Agent_Integration_Dev) + +### Task 2.1 - Agent_UI_Specialist: LeafletJS for Spatial Visualization +Objective: Integrate interactive maps for visualizing GeoJSON outputs from Phase 0 spatial tools. + +1. Set up LeafletJS integration. + - Install Leaflet and React-Leaflet packages + - Configure Leaflet CSS and map container components + - Set up base map tiles (OpenStreetMap) for offline compatibility + - Create reusable map component with zoom and pan controls +2. Implement GeoJSON rendering for Phase 0 outputs. + - Add GeoJSON layer support for outputs from Transform and Processing tools + - Style different geometry types (Point, LineString, Polygon) with distinct colors + - Implement popup displays for feature properties and metadata + - Add fit-to-bounds functionality for automatic zoom to data extent +3. Enhanced spatial output viewer. + - Integrate map as new tab in multi-format output viewer + - Support switching between map view and raw JSON view + - Add map export functionality (PNG screenshot) + - Test with outputs from Phase 0 spatial transformation tools + +### Task 2.2 - Agent_Integration_Dev: Multi-Format Input and Output Viewers +Objective: Enhance input/output handling to support diverse Phase 0 tool formats. + +1. Enhanced input system. + - Add file upload support for GeoJSON, JSON, and text files + - Integrate Phase 0 sample data as selectable input options + - Implement drag-and-drop file interface + - Create input validation for different file formats used by Phase 0 +2. Advanced output viewers for Phase 0 tool types. + - Implement chart visualization for time series data from Analysis tools + - Add table viewer for structured data with sorting and filtering + - Create histogram visualization for Phase 0 Statistics tool outputs + - Support CSV export for tabular data outputs from I/O tools +3. Data format conversion utilities. + - Create utilities for converting between formats supported by Phase 0 + - Implement data validation and format verification + - Add data preview capabilities for large datasets + - Create format-specific error handling and user feedback + +### Task 2.3 - Agent_UI_Specialist: Enhanced Search with Fuzzy Matching +Objective: Implement intelligent search capabilities for Phase 0 tool discovery. + +1. Implement fuzzy search engine. + - Install and configure Fuse.js for fuzzy string matching + - Create search index from Phase 0 tool names, descriptions, and categories + - Configure search weights and matching thresholds + - Implement search result ranking and relevance scoring +2. Advanced search interface. + - Add search suggestions based on Phase 0 tool categories + - Implement search history and recent searches + - Create advanced search filters (by parameter count, output type) + - Add search result highlighting and match explanation +3. Search performance optimization. + - Implement search result caching for repeated queries + - Add debounced search input to reduce unnecessary operations + - Optimize search index for Phase 0 tool catalog size + - Create search analytics for usage tracking + +## Phase 3: Create Deployable Instance - Agent Group Delta (Agent_Backend_Dev, Agent_Integration_Dev) + +### Task 3.1 - Agent_Backend_Dev: MCP (Model Context Protocol) Support +Objective: Implement MCP support to enable AI model integration and Phase 0 tool execution via protocol. + +1. Research and implement MCP protocol. + - Study MCP specification for tool integration patterns + - Implement MCP server endpoints for Phase 0 tool discovery and execution + - Create MCP-compatible tool metadata format from Phase 0 index.json + - Support MCP client connections and protocol negotiation +2. MCP tool execution interface. + - Create MCP endpoint for Phase 0 tool listing and metadata retrieval + - Implement MCP tool execution endpoint with parameter validation + - Support streaming responses for long-running Phase 0 tools + - Add MCP error handling and status reporting +3. MCP client testing and validation. + - Create MCP client test suite for protocol validation + - Test integration with Claude and other MCP-compatible clients + - Validate Phase 0 tool execution through MCP interface + - Document MCP integration examples and usage patterns + +### Task 3.2 - Agent_Integration_Dev: REST API Documentation +Objective: Create comprehensive REST API documentation for external integrations with Phase 0 tools. + +1. API specification development. + - Document all REST endpoints for Phase 0 tool discovery and execution + - Create OpenAPI/Swagger specification for API endpoints + - Include request/response schemas based on Phase 0 tool metadata + - Add authentication and error handling documentation +2. Interactive API documentation. + - Set up Swagger UI for interactive API exploration + - Provide example requests and responses for Phase 0 tools + - Include API testing interface within documentation + - Create API usage examples and integration guides +3. API client libraries and SDKs. + - Create JavaScript/TypeScript client library for API integration + - Provide Python client library for backend integrations + - Generate client code from OpenAPI specification + - Create integration examples for common Phase 0 tool use cases + +### Task 3.3 - Agent_Backend_Dev: Create Deployable ToolVault Instance +Objective: Create a production-ready ToolVault instance that VS Code "Command Toolbox" can run against. + +1. Backend service architecture. + - Set up Node.js/Express server for API endpoints + - Implement tool discovery and execution REST API for Phase 0 tools + - Create middleware for request logging, CORS, and error handling + - Add health check and monitoring endpoints +2. Service deployment configuration. + - Create Docker containerization including Phase 0 bundle assets + - Set up environment configuration management + - Implement logging and monitoring systems + - Configure service discovery and load balancing support +3. VS Code integration preparation. + - Design API endpoints specific to VS Code Command Toolbox requirements + - Implement authentication/authorization for VS Code extension + - Create WebSocket support for real-time Phase 0 tool execution updates + - Add extension-specific metadata and configuration endpoints + +### Task 3.4 - Agent_Integration_Dev: Instance Maturation for VS Code Development +Objective: Maintain and evolve the deployable instance to support ongoing VS Code extension development. + +1. Monitoring and maintenance setup. + - Implement application performance monitoring (APM) for Phase 0 tool execution + - Set up error tracking and alerting systems + - Create automated backup and recovery procedures + - Add usage analytics and metrics collection for tool usage patterns +2. Development environment support. + - Create staging/development instance configurations + - Implement feature flagging for experimental Phase 0 tool features + - Set up continuous integration/deployment pipelines + - Add developer tools and debugging endpoints +3. Evolution and feedback integration. + - Create feedback collection mechanisms from VS Code extension usage + - Implement A/B testing framework for Phase 0 tool feature experiments + - Add configuration management for different deployment environments + - Maintain backward compatibility while adding new Phase 0 tool features + +## Memory Bank System + +Memory Bank System: Single file [Memory_Bank.md](Memory_Bank.md) - All agents should log their work, decisions, and important findings to this centralized memory bank for project continuity. + +--- + +## Note on Handover Protocol + +For long-running projects or situations requiring context transfer (e.g., exceeding LLM context limits, changing specialized agents), the APM Handover Protocol should be initiated. This ensures smooth transitions and preserves project knowledge. Detailed procedures are outlined in the framework guide: + +`prompts/01_Manager_Agent_Core_Guides/05_Handover_Protocol_Guide.md` + +The current Manager Agent or the User should initiate this protocol as needed. \ No newline at end of file From d323d66ea835faa2273822721714183a9ecced68 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 14:44:55 +0100 Subject: [PATCH 05/18] delete legacy prompts --- prompts/tasks/Task_1.1_React_Project_Init.md | 132 --- prompts/tasks/Task_1.2_MUI_Setup.md | 150 ---- prompts/tasks/Task_1.3_Testing_Config.md | 217 ----- .../tasks/Task_1.4_TypeScript_Interfaces.md | 153 ---- prompts/tasks/Task_2.1_Data_Service_Layer.md | 177 ---- prompts/tasks/Task_2.2_Tool_List_View.md | 202 ----- prompts/tasks/Task_2.3_Tool_Detail_View.md | 272 ------ prompts/tasks/Task_2.4_Routing_Navigation.md | 295 ------ prompts/tasks/Task_3.1_Unit_Testing.md | 331 ------- prompts/tasks/Task_3.2_E2E_Testing.md | 418 --------- .../Task_3.3_Performance_Optimization.md | 472 ---------- prompts/tasks/Task_3.4_Error_Handling.md | 551 ------------ prompts/tasks/Task_3.5_Accessibility.md | 723 --------------- prompts/tasks/Task_4.1_Mock_Backend.md | 476 ---------- prompts/tasks/Task_4.2_Enhanced_Search.md | 677 -------------- prompts/tasks/Task_4.3_Output_Rendering.md | 845 ------------------ prompts/tasks/Task_4.4_Tool_Execution.md | 768 ---------------- prompts/tasks/Task_4.5_Responsive_Design.md | 765 ---------------- .../Task_5.1_Integrate_Real_Tool_Modules.md | 49 - ...Task_5.2_Dynamic_Tool_Loading_Execution.md | 55 -- .../Task_5.3_Error_Handling_Dev_Experience.md | 49 - .../Task_5.4_Minimal_Catalog_Example_Tools.md | 50 -- prompts/tasks/Task_5.5_Additional_JS_Tools.md | 51 -- prompts/tasks/Task_Issue_1.md | 183 ---- 24 files changed, 8061 deletions(-) delete mode 100644 prompts/tasks/Task_1.1_React_Project_Init.md delete mode 100644 prompts/tasks/Task_1.2_MUI_Setup.md delete mode 100644 prompts/tasks/Task_1.3_Testing_Config.md delete mode 100644 prompts/tasks/Task_1.4_TypeScript_Interfaces.md delete mode 100644 prompts/tasks/Task_2.1_Data_Service_Layer.md delete mode 100644 prompts/tasks/Task_2.2_Tool_List_View.md delete mode 100644 prompts/tasks/Task_2.3_Tool_Detail_View.md delete mode 100644 prompts/tasks/Task_2.4_Routing_Navigation.md delete mode 100644 prompts/tasks/Task_3.1_Unit_Testing.md delete mode 100644 prompts/tasks/Task_3.2_E2E_Testing.md delete mode 100644 prompts/tasks/Task_3.3_Performance_Optimization.md delete mode 100644 prompts/tasks/Task_3.4_Error_Handling.md delete mode 100644 prompts/tasks/Task_3.5_Accessibility.md delete mode 100644 prompts/tasks/Task_4.1_Mock_Backend.md delete mode 100644 prompts/tasks/Task_4.2_Enhanced_Search.md delete mode 100644 prompts/tasks/Task_4.3_Output_Rendering.md delete mode 100644 prompts/tasks/Task_4.4_Tool_Execution.md delete mode 100644 prompts/tasks/Task_4.5_Responsive_Design.md delete mode 100644 prompts/tasks/Task_5.1_Integrate_Real_Tool_Modules.md delete mode 100644 prompts/tasks/Task_5.2_Dynamic_Tool_Loading_Execution.md delete mode 100644 prompts/tasks/Task_5.3_Error_Handling_Dev_Experience.md delete mode 100644 prompts/tasks/Task_5.4_Minimal_Catalog_Example_Tools.md delete mode 100644 prompts/tasks/Task_5.5_Additional_JS_Tools.md delete mode 100644 prompts/tasks/Task_Issue_1.md diff --git a/prompts/tasks/Task_1.1_React_Project_Init.md b/prompts/tasks/Task_1.1_React_Project_Init.md deleted file mode 100644 index 3157b94..0000000 --- a/prompts/tasks/Task_1.1_React_Project_Init.md +++ /dev/null @@ -1,132 +0,0 @@ -# APM Task Assignment: Initialize React TypeScript Project - -## 1. Agent Role & APM Context - -**Introduction:** You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. - -**Your Role:** As an Implementation Agent, you will execute specific tasks assigned to you based on the detailed project plan. Your responsibilities include understanding requirements, performing necessary actions (writing code, configuring systems), and meticulously documenting your work. - -**Workflow:** You will receive task assignments from the User (prepared by the Manager Agent). You interact directly with the User and must log all significant activities to the Memory Bank upon task completion. The Memory Bank serves as the project's official log for tracking progress and maintaining context. - -**Onboarding Reference:** For detailed information about the APM framework and your role, refer to `prompts/02_Utility_Prompts_And_Format_Definitions/Imlementation_Agent_Onboarding.md`. - -## 2. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 1, Task 1.1** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Set up the foundational React project with TypeScript, Vite, and pnpm package manager. - -**Detailed Action Steps:** - -1. **Initialize Vite React TypeScript project:** - - Navigate to the ToolVault root directory - - Run `pnpm create vite client --template react-ts` - - Navigate to the newly created `client` directory - - Run `pnpm install` to install all dependencies - - Verify the development server runs correctly with `pnpm dev` - - Ensure the default React app loads at http://localhost:5173 - -2. **Configure TypeScript for strict mode:** - - Open `client/tsconfig.json` - - Ensure the following compiler options are set: - ```json - { - "compilerOptions": { - "strict": true, - "noImplicitAny": true, - "strictNullChecks": true, - "strictFunctionTypes": true, - "strictBindCallApply": true, - "strictPropertyInitialization": true, - "noImplicitThis": true, - "alwaysStrict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - } - } - ``` - - Configure path aliases for cleaner imports by adding: - ```json - "baseUrl": "./src", - "paths": { - "@components/*": ["components/*"], - "@utils/*": ["utils/*"], - "@types/*": ["types/*"], - "@hooks/*": ["hooks/*"], - "@services/*": ["services/*"] - } - ``` - - Update `vite.config.ts` to support these path aliases using the `vite-tsconfig-paths` plugin: - ```bash - pnpm add -D vite-tsconfig-paths - ``` - -3. **Set up project directory structure:** - - Create the following directories inside `client/src/`: - - `components/` - For React components - - `types/` - For TypeScript interfaces and types - - `utils/` - For utility functions - - `hooks/` - For custom React hooks - - `services/` - For API/data fetching logic - - Create `client/public/data/` directory for the static index.json file - - Copy the sample `index.json` from `samples/index.json` to `client/public/data/index.json` - -4. **Configure development environment:** - - Create `client/.env` file with initial environment variables: - ``` - VITE_APP_TITLE=ToolVault - VITE_API_BASE_URL=/data - ``` - - Update `client/vite.config.ts` to ensure proper asset handling and HMR - - Configure build optimization settings for production - - Add a `.gitignore` entry for `.env.local` if not already present - -**Additional Context:** -- The project uses pnpm as the package manager (not npm or yarn) -- TypeScript strict mode is essential for catching type errors early -- The path aliases will make imports cleaner throughout the project -- The sample index.json in `samples/` contains the data structure we'll be working with - -## 3. Expected Output & Deliverables - -**Define Success:** -- A fully initialized React TypeScript project with Vite -- TypeScript configured in strict mode with path aliases -- Proper directory structure created -- Development server running successfully -- Sample index.json copied to the public/data directory - -**Specific Deliverables:** -1. Created `client/` directory with React TypeScript Vite project -2. Modified `tsconfig.json` with strict mode and path aliases -3. Updated `vite.config.ts` with path alias support -4. Created all required subdirectories in `src/` -5. Created `.env` file with initial variables -6. Copied `index.json` to `public/data/` -7. Verification that `pnpm dev` runs without errors - -## 4. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion of this task, you **must** log your work comprehensively to the Memory Bank file at: -`Memory/Phase_1_Project_Setup_Infrastructure/Task_1.1_React_Project_Init_Log.md` - -**Format Adherence:** Adhere strictly to the format defined in `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Your log must include: -- Agent identifier (Agent_Setup_Specialist) -- Task reference (Phase 1 / Task 1.1) -- Clear summary of actions taken -- Key configuration choices made -- Confirmation of successful setup (dev server running) -- Any issues encountered and their resolutions - -**Important:** Be concise yet informative. Focus on key actions and outcomes rather than verbose step-by-step descriptions. - -## 5. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. Key areas to confirm: -- Location of the ToolVault root directory -- Access to pnpm package manager -- Location of sample files -- Any permission issues with creating directories - -Please acknowledge receipt of this task assignment and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_1.2_MUI_Setup.md b/prompts/tasks/Task_1.2_MUI_Setup.md deleted file mode 100644 index a5bdf42..0000000 --- a/prompts/tasks/Task_1.2_MUI_Setup.md +++ /dev/null @@ -1,150 +0,0 @@ -# APM Task Assignment: Install and Configure Material-UI - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 1, Task 1.2** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Integrate Material-UI (MUI) component library with custom theming for the ToolVault application. - -**Prerequisites:** This task builds upon Task 1.1. Ensure the React TypeScript project is initialized in the `client/` directory with the development server running successfully. - -## 2. Detailed Action Steps - -1. **Install Material-UI dependencies:** - - Navigate to the `client/` directory - - Install core MUI packages: - ```bash - pnpm add @mui/material @emotion/react @emotion/styled - ``` - - Install MUI icons package: - ```bash - pnpm add @mui/icons-material - ``` - - Add Roboto font to `client/index.html` in the `` section: - ```html - - - - ``` - -2. **Create custom MUI theme:** - - Create directory `client/src/theme/` - - Create `client/src/theme/theme.ts` with ToolVault branding: - ```typescript - import { createTheme } from '@mui/material/styles'; - - export const theme = createTheme({ - palette: { - primary: { - main: '#1976d2', - light: '#42a5f5', - dark: '#1565c0', - }, - secondary: { - main: '#dc004e', - light: '#e33371', - dark: '#9a0036', - }, - background: { - default: '#f5f5f5', - paper: '#ffffff', - }, - }, - typography: { - fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', - h1: { - fontSize: '2.5rem', - fontWeight: 500, - }, - h2: { - fontSize: '2rem', - fontWeight: 500, - }, - }, - spacing: 8, - shape: { - borderRadius: 8, - }, - breakpoints: { - values: { - xs: 0, - sm: 600, - md: 900, - lg: 1200, - xl: 1536, - }, - }, - }); - ``` - -3. **Set up theme provider:** - - Update `client/src/main.tsx` to wrap the app with ThemeProvider: - ```typescript - import { ThemeProvider } from '@mui/material/styles'; - import CssBaseline from '@mui/material/CssBaseline'; - import { theme } from './theme/theme'; - ``` - - Wrap the App component: - ```typescript - - - - - - - ``` - - Create `client/src/contexts/ThemeContext.tsx` for future theme switching capability (stub implementation for now) - -4. **Create base layout components:** - - Create `client/src/components/layout/` directory - - Create `AppBar.tsx` with ToolVault branding: - - Use MUI AppBar and Toolbar components - - Add ToolVault title/logo - - Include placeholder for navigation items - - Create `NavigationDrawer.tsx`: - - Responsive drawer component using MUI Drawer - - Include placeholder menu items - - Create `Footer.tsx`: - - Simple footer with copyright and version info - - Create `MainLayout.tsx`: - - Combine AppBar, NavigationDrawer, main content area, and Footer - - Use MUI Container for proper spacing - - Implement responsive behavior - -## 3. Expected Output & Deliverables - -**Success Criteria:** -- Material-UI fully integrated with the React project -- Custom theme applied consistently across the application -- Base layout components created and rendering correctly -- No TypeScript errors with strict mode enabled - -**Deliverables:** -1. Installed MUI packages in package.json -2. Created `theme/theme.ts` with custom theme configuration -3. Updated `main.tsx` with ThemeProvider and CssBaseline -4. Created layout components (AppBar, NavigationDrawer, Footer, MainLayout) -5. Roboto font integrated in index.html -6. Application runs without errors and displays MUI components - -## 4. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_1_Project_Setup_Infrastructure/Task_1.2_MUI_Setup_Log.md` - -**Format:** Follow the format in `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Setup_Specialist) -- Task reference (Phase 1 / Task 1.2) -- Summary of MUI integration -- Theme configuration choices -- Created components list -- Verification that the UI renders correctly - -## 5. Clarification Instruction - -If any part of this task is unclear, please ask before proceeding. Key areas: -- Specific branding colors or requirements -- Additional MUI components needed -- Layout specifications or mockups - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_1.3_Testing_Config.md b/prompts/tasks/Task_1.3_Testing_Config.md deleted file mode 100644 index c385f85..0000000 --- a/prompts/tasks/Task_1.3_Testing_Config.md +++ /dev/null @@ -1,217 +0,0 @@ -# APM Task Assignment: Configure Testing Infrastructure - -## 1. Agent Role & APM Context - -**Introduction:** You are activated as Agent_Testing_Config within the APM framework for the ToolVault project. - -**Your Role:** As a specialized testing configuration agent, you will set up comprehensive testing infrastructure including unit tests, component tests, and end-to-end tests for the React application. - -## 2. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 1, Task 1.3** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Set up comprehensive testing framework with Jest, React Testing Library, and Playwright. - -**Prerequisites:** Tasks 1.1 and 1.2 should be completed (React project initialized with MUI configured). - -## 3. Detailed Action Steps - -1. **Install and configure unit testing dependencies:** - - Navigate to the `client/` directory - - Install Jest and React Testing Library: - ```bash - pnpm add -D jest @testing-library/react @testing-library/jest-dom @testing-library/user-event - ``` - - Install TypeScript Jest support: - ```bash - pnpm add -D ts-jest @types/jest - ``` - - Create `client/jest.config.js`: - ```javascript - module.exports = { - preset: 'ts-jest', - testEnvironment: 'jsdom', - roots: ['/src'], - setupFilesAfterEnv: ['/src/test-utils/setupTests.ts'], - moduleNameMapper: { - '^@components/(.*)$': '/src/components/$1', - '^@utils/(.*)$': '/src/utils/$1', - '^@types/(.*)$': '/src/types/$1', - '^@hooks/(.*)$': '/src/hooks/$1', - '^@services/(.*)$': '/src/services/$1', - }, - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.d.ts', - '!src/main.tsx', - '!src/vite-env.d.ts', - ], - coverageThreshold: { - global: { - branches: 80, - functions: 80, - lines: 80, - statements: 80, - }, - }, - }; - ``` - -2. **Set up component testing utilities:** - - Create `client/src/test-utils/` directory - - Create `setupTests.ts`: - ```typescript - import '@testing-library/jest-dom'; - ``` - - Create `test-utils.tsx` with custom render function: - ```typescript - import { ReactElement } from 'react'; - import { render, RenderOptions } from '@testing-library/react'; - import { ThemeProvider } from '@mui/material/styles'; - import { theme } from '../theme/theme'; - - const AllTheProviders = ({ children }: { children: React.ReactNode }) => { - return ( - - {children} - - ); - }; - - const customRender = ( - ui: ReactElement, - options?: Omit, - ) => render(ui, { wrapper: AllTheProviders, ...options }); - - export * from '@testing-library/react'; - export { customRender as render }; - ``` - - Create mock data generators in `client/src/test-utils/mockData.ts` - - Install and configure MSW for API mocking: - ```bash - pnpm add -D msw - ``` - -3. **Configure E2E testing with Playwright:** - - Install Playwright: - ```bash - pnpm add -D @playwright/test - ``` - - Create `client/playwright.config.ts`: - ```typescript - import { defineConfig, devices } from '@playwright/test'; - - export default defineConfig({ - testDir: './e2e', - fullyParallel: true, - forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, - reporter: 'html', - use: { - baseURL: 'http://localhost:5173', - trace: 'on-first-retry', - }, - projects: [ - { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, - }, - { - name: 'firefox', - use: { ...devices['Desktop Firefox'] }, - }, - { - name: 'webkit', - use: { ...devices['Desktop Safari'] }, - }, - ], - webServer: { - command: 'pnpm dev', - url: 'http://localhost:5173', - reuseExistingServer: !process.env.CI, - }, - }); - ``` - - Create `client/e2e/` directory for E2E tests - - Set up page objects pattern in `client/e2e/pages/` - - Configure visual regression testing capabilities - -4. **Create testing scripts and CI configuration:** - - Update `client/package.json` with test scripts: - ```json - { - "scripts": { - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage", - "test:e2e": "playwright test", - "test:e2e:ui": "playwright test --ui" - } - } - ``` - - Create `.github/workflows/ci.yml` for GitHub Actions: - ```yaml - name: CI - on: [push, pull_request] - jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2 - - uses: actions/setup-node@v3 - with: - node-version: '18' - cache: 'pnpm' - - run: pnpm install - - run: pnpm test:coverage - - run: pnpm build - - run: pnpm test:e2e - ``` - - Install and configure Husky for pre-commit hooks: - ```bash - pnpm add -D husky - pnpm exec husky init - ``` - - Configure coverage thresholds at 80% minimum - -## 4. Expected Output & Deliverables - -**Success Criteria:** -- Jest configured with TypeScript support -- React Testing Library integrated with custom render -- Playwright configured for E2E testing -- CI/CD pipeline configured -- All test commands working correctly - -**Deliverables:** -1. Jest configuration file -2. Test utilities and setup files -3. Playwright configuration -4. GitHub Actions workflow -5. Updated package.json with test scripts -6. Husky pre-commit hooks -7. Sample test files demonstrating the setup works - -## 5. Memory Bank Logging Instructions - -**Instruction:** Log your work to: -`Memory/Phase_1_Project_Setup_Infrastructure/Task_1.3_Testing_Config_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Testing_Config) -- Task reference (Phase 1 / Task 1.3) -- Testing frameworks configured -- Coverage thresholds set -- CI/CD pipeline status -- Any configuration challenges resolved - -## 6. Clarification Instruction - -Please ask if you need clarification on: -- Specific testing requirements -- Coverage threshold adjustments -- Additional testing tools needed - -Acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_1.4_TypeScript_Interfaces.md b/prompts/tasks/Task_1.4_TypeScript_Interfaces.md deleted file mode 100644 index fe3f906..0000000 --- a/prompts/tasks/Task_1.4_TypeScript_Interfaces.md +++ /dev/null @@ -1,153 +0,0 @@ -# APM Task Assignment: Create TypeScript Interfaces from Schema - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 1, Task 1.4** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Define TypeScript interfaces based on the provided index.schema.json. - -**Prerequisites:** Tasks 1.1-1.3 completed. The React TypeScript project should be set up with strict mode enabled. - -## 2. Detailed Action Steps - -1. **Analyze the JSON schema structure:** - - Review `samples/index.schema.json` thoroughly - - Review `samples/index.json` for example data - - Identify all data types and their relationships: - - Root object (ToolVaultIndex) - - Tool objects with inputs/outputs - - Input/Output parameter definitions - - Note required vs optional fields for each type - -2. **Create core type definitions:** - - Create `client/src/types/index.ts` as the main type export file - - Define interfaces matching the schema exactly: - ```typescript - // ToolVault Index root interface - export interface ToolVaultIndex { - name: string; - version: string; - description?: string; - updated?: string; // ISO 8601 date-time string - tools: Tool[]; - } - - // Individual tool interface - export interface Tool { - id: string; - name: string; - description: string; - category?: string; - tags?: string[]; - inputs: ToolInput[]; - outputs: ToolOutput[]; - } - - // Input parameter interface - export interface ToolInput { - name: string; - label?: string; - type: InputType; - required?: boolean; - } - - // Output parameter interface - export interface ToolOutput { - name: string; - label?: string; - type: OutputType; - } - ``` - - Create type unions for input/output types: - ```typescript - export type InputType = 'string' | 'number' | 'integer' | 'boolean' | 'geojson' | 'file'; - export type OutputType = 'string' | 'number' | 'integer' | 'boolean' | 'geojson' | 'file' | 'image'; - ``` - - Add any additional utility types needed - -3. **Implement type guards and validators:** - - Create `client/src/utils/typeGuards.ts`: - ```typescript - import { Tool, ToolInput, ToolOutput, ToolVaultIndex } from '@types/index'; - - export function isToolVaultIndex(data: unknown): data is ToolVaultIndex { - // Implementation to validate the structure - } - - export function isTool(data: unknown): data is Tool { - // Implementation to validate tool structure - } - ``` - - Create `client/src/utils/validators.ts` for runtime validation: - - Implement JSON schema validation using the actual schema file - - Create validation functions that return detailed error messages - - Consider using a library like `ajv` for JSON schema validation: - ```bash - pnpm add ajv - ``` - -4. **Generate mock data factories:** - - Create `client/src/test-utils/factories.ts`: - ```typescript - import { Tool, ToolInput, ToolOutput, ToolVaultIndex } from '@types/index'; - - export function createMockToolVaultIndex(overrides?: Partial): ToolVaultIndex { - // Factory function that generates valid test data - } - - export function createMockTool(overrides?: Partial): Tool { - // Factory function for individual tools - } - - export function createMockToolInput(overrides?: Partial): ToolInput { - // Factory function for inputs - } - ``` - - Ensure factories respect all schema constraints - - Generate realistic test data based on the sample index.json - -## 3. Expected Output & Deliverables - -**Success Criteria:** -- TypeScript interfaces exactly match the JSON schema -- Type guards provide runtime type safety -- Validators can check data against the schema -- Mock factories generate valid test data -- No TypeScript errors in strict mode - -**Deliverables:** -1. `types/index.ts` with all interfaces and type definitions -2. `utils/typeGuards.ts` with type predicate functions -3. `utils/validators.ts` with runtime validation -4. `test-utils/factories.ts` with mock data generators -5. All exports properly configured for use throughout the app - -## 4. Memory Bank Logging Instructions - -**Instruction:** Log your work to: -`Memory/Phase_1_Project_Setup_Infrastructure/Task_1.4_TypeScript_Interfaces_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Setup_Specialist) -- Task reference (Phase 1 / Task 1.4) -- List of created interfaces and types -- Validation approach chosen -- Any schema interpretation decisions made -- Confirmation that types work with strict mode - -## 5. Additional Context - -**Important Schema Considerations:** -- The `updated` field uses ISO 8601 date-time format -- Input/output types are constrained to specific values -- The `required` field on inputs defaults to false when not specified -- Consider future extensibility when designing the types - -## 6. Clarification Instruction - -If you need clarification on: -- Schema interpretation -- Additional type requirements -- Validation library preferences - -Please ask before proceeding. Acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_2.1_Data_Service_Layer.md b/prompts/tasks/Task_2.1_Data_Service_Layer.md deleted file mode 100644 index ef132d6..0000000 --- a/prompts/tasks/Task_2.1_Data_Service_Layer.md +++ /dev/null @@ -1,177 +0,0 @@ -# APM Task Assignment: Implement Data Service Layer - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 2, Task 2.1** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Create service layer for fetching and managing index.json data with React Query integration. - -**Prerequisites:** Phase 1 tasks completed successfully. The React TypeScript project should have MUI configured, testing infrastructure set up, and TypeScript interfaces generated from the schema. - -## 2. Detailed Action Steps - -1. **Create data fetching service:** - - Create `src/services/toolVaultService.ts` with the following functions: - ```typescript - export async function fetchToolVaultIndex(): Promise { - // Fetch from /data/index.json endpoint - // Add error handling for network failures - // Implement retry logic with exponential backoff - } - ``` - - Add proper error handling for network failures (404, 500, timeout) - - Implement retry logic with exponential backoff using a utility like `async-retry`: - ```bash - pnpm add async-retry - pnpm add -D @types/async-retry - ``` - - Use the `parseToolVaultIndex` validator from `utils/validators.ts` to ensure type safety - - Create custom error classes for different failure types (NetworkError, ValidationError, etc.) - -2. **Install and configure React Query:** - - Install React Query (TanStack Query): - ```bash - pnpm add @tanstack/react-query - ``` - - Create `src/contexts/QueryClientProvider.tsx`: - ```typescript - import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; - import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; - ``` - - Configure QueryClient with optimal settings: - - Default staleTime: 5 minutes - - Default cacheTime: 10 minutes - - Retry failed requests 3 times - - Enable devtools in development - - Update `src/main.tsx` to wrap the app with QueryClientProvider - -3. **Create React hooks for data access:** - - Create `src/hooks/useToolVaultData.ts`: - ```typescript - export function useToolVaultData() { - return useQuery({ - queryKey: ['toolVaultIndex'], - queryFn: fetchToolVaultIndex, - // Add loading and error states - // Include data transformation if needed - }); - } - ``` - - Create `src/hooks/useToolById.ts`: - ```typescript - export function useToolById(toolId: string) { - // Derived from useToolVaultData - // Return specific tool by ID - // Handle case when tool is not found - } - ``` - - Add proper TypeScript typing for all hooks - - Include JSDoc comments explaining hook usage - -4. **Implement caching and data management:** - - Configure React Query cache invalidation strategies - - Add `refetch` functionality for manual data refresh - - Implement optimistic updates support (for future use) - - Create cache utilities in `src/utils/cache.ts` if needed - - Add query invalidation patterns for data updates - -## 3. Expected Output & Deliverables - -**Success Criteria:** -- Data service successfully fetches and validates index.json -- React Query properly integrated with QueryClient configuration -- Custom hooks provide clean data access API -- Error handling covers all failure scenarios -- Type safety maintained throughout the service layer - -**Deliverables:** -1. `services/toolVaultService.ts` with fetch functions and error handling -2. `contexts/QueryClientProvider.tsx` with QueryClient setup -3. `hooks/useToolVaultData.ts` and `hooks/useToolById.ts` -4. Updated `main.tsx` with QueryClientProvider -5. Custom error classes and utilities -6. Comprehensive tests for all service functions - -## 4. Testing Requirements - -Create tests for: -- **Service Layer Tests** (`services/toolVaultService.test.ts`): - - Test successful data fetching - - Test network error handling - - Test data validation failures - - Test retry logic behavior - - Mock fetch responses using MSW - -- **Hook Tests** (`hooks/useToolVaultData.test.ts`): - - Test loading states - - Test successful data loading - - Test error states - - Test data transformation - - Use React Testing Library's `renderHook` - -- **Integration Tests**: - - Test complete data flow from service to components - - Verify React Query cache behavior - - Test query invalidation - -## 5. Implementation Notes - -**Error Handling Strategy:** -```typescript -export class ToolVaultError extends Error { - constructor(message: string, public code: string, public details?: any) { - super(message); - this.name = 'ToolVaultError'; - } -} - -export class NetworkError extends ToolVaultError { - constructor(message: string, public status?: number) { - super(message, 'NETWORK_ERROR', { status }); - this.name = 'NetworkError'; - } -} -``` - -**Retry Configuration:** -- Use exponential backoff: 1s, 2s, 4s delays -- Only retry on 5xx errors and network failures -- Don't retry on 4xx client errors - -**React Query Configuration:** -```typescript -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - staleTime: 5 * 60 * 1000, // 5 minutes - cacheTime: 10 * 60 * 1000, // 10 minutes - retry: 3, - refetchOnWindowFocus: false, - }, - }, -}); -``` - -## 6. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_2_Core_UI_Implementation/Task_2.1_Data_Service_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Frontend_Dev) -- Task reference (Phase 2 / Task 2.1) -- Summary of service architecture implemented -- React Query configuration choices -- Error handling strategy -- Test coverage achieved -- Any performance considerations - -## 7. Clarification Instruction - -If any part of this task is unclear, please ask before proceeding. Key areas: -- React Query version preferences -- Specific error handling requirements -- Cache invalidation strategies -- Testing approach preferences - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_2.2_Tool_List_View.md b/prompts/tasks/Task_2.2_Tool_List_View.md deleted file mode 100644 index d2061ec..0000000 --- a/prompts/tasks/Task_2.2_Tool_List_View.md +++ /dev/null @@ -1,202 +0,0 @@ -# APM Task Assignment: Build Tool List View Component - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 2, Task 2.2** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Create the main tool browsing interface with search and filter capabilities using Material-UI components. - -**Prerequisites:** Task 2.1 completed - Data service layer with React Query integration should be functional. - -## 2. Detailed Action Steps - -1. **Create ToolList component structure:** - - Create `src/components/tools/ToolList.tsx` as the main container component: - ```typescript - interface ToolListProps { - // Optional props for external filtering - categoryFilter?: string; - searchQuery?: string; - } - - export const ToolList: React.FC = (props) => { - // Use useToolVaultData hook from Task 2.1 - // Implement responsive grid layout using MUI Grid - // Handle loading, error, and empty states - }; - ``` - - Create `src/components/tools/ToolCard.tsx` for individual tool display: - - Display tool name, description, and category - - Show tags as MUI Chips - - Include hover effects and click handlers - - Use MUI Card, CardContent, CardActions components - - Add "View Details" button linking to detail view - -2. **Implement search functionality:** - - Create `src/components/tools/SearchBar.tsx`: - ```typescript - interface SearchBarProps { - value: string; - onChange: (value: string) => void; - placeholder?: string; - } - ``` - - Use MUI TextField with InputAdornment for search icon - - Implement real-time search with 300ms debouncing using `useDebouncedValue` hook - - Search across tool name, description, and tags - - Highlight search matches in results using `src/utils/searchUtils.ts`: - ```typescript - export function highlightMatches(text: string, query: string): ReactNode; - export function searchTools(tools: Tool[], query: string): Tool[]; - ``` - - Add clear search functionality with X button - -3. **Build filtering system:** - - Create `src/components/tools/FilterPanel.tsx`: - ```typescript - interface FilterPanelProps { - categories: string[]; - selectedCategories: string[]; - onCategoryChange: (categories: string[]) => void; - availableTags: string[]; - selectedTags: string[]; - onTagChange: (tags: string[]) => void; - } - ``` - - Use MUI Accordion for collapsible filter sections: - - Category filter with checkboxes (FormGroup, FormControlLabel, Checkbox) - - Tag-based filtering with autocomplete chips (Autocomplete component) - - "Clear all filters" functionality - - Extract unique categories and tags from the tools data - - Persist filter state in URL query parameters (using React Router) - -4. **Add sorting capabilities:** - - Create `src/components/tools/SortControls.tsx`: - - Dropdown using MUI Select with sorting options: - - Name (A-Z, Z-A) - - Category (A-Z, Z-A) - - Recently updated (if timestamp available) - - Include sort direction toggle buttons - - Implement sorting logic in `src/utils/sortUtils.ts`: - ```typescript - export type SortOption = 'name-asc' | 'name-desc' | 'category-asc' | 'category-desc'; - export function sortTools(tools: Tool[], sortOption: SortOption): Tool[]; - ``` - - Persist sort preferences in localStorage - -## 3. State Management and Performance - -**Local State Structure:** -```typescript -interface ToolListState { - searchQuery: string; - selectedCategories: string[]; - selectedTags: string[]; - sortOption: SortOption; - currentPage: number; - itemsPerPage: number; -} -``` - -**Performance Optimizations:** -- Use `useMemo` for filtering and sorting expensive operations -- Implement virtual scrolling or pagination for large tool lists (MUI Pagination) -- Lazy load tool icons/images if present -- Debounce search input to avoid excessive filtering - -## 4. Responsive Design Requirements - -- **Desktop (≥1200px)**: 4 tools per row, sidebar filters -- **Tablet (768px-1199px)**: 3 tools per row, collapsible filters -- **Mobile (≤767px)**: 1-2 tools per row, bottom sheet filters - -Use MUI breakpoints and Grid system: -```typescript - - - - - -``` - -## 5. Expected Output & Deliverables - -**Success Criteria:** -- Tool list displays all tools from index.json correctly -- Search functionality works across name, description, and tags -- Filtering by categories and tags functions properly -- Sorting options work and persist across sessions -- Responsive design works on all screen sizes -- Loading and error states are handled gracefully - -**Deliverables:** -1. `components/tools/ToolList.tsx` - Main container component -2. `components/tools/ToolCard.tsx` - Individual tool display -3. `components/tools/SearchBar.tsx` - Search input with debouncing -4. `components/tools/FilterPanel.tsx` - Category and tag filtering -5. `components/tools/SortControls.tsx` - Sort dropdown and controls -6. `utils/searchUtils.ts` - Search and highlighting utilities -7. `utils/sortUtils.ts` - Sorting logic -8. `hooks/useDebouncedValue.ts` - Debouncing hook -9. Comprehensive tests for all components - -## 6. Testing Requirements - -Create tests for: -- **ToolList.test.tsx**: - - Renders tool list correctly - - Handles loading and error states - - Integrates search, filter, and sort functionality - - Responsive behavior - -- **ToolCard.test.tsx**: - - Displays tool information correctly - - Click handlers work properly - - Chip rendering for tags - -- **SearchBar.test.tsx**: - - Search input debouncing - - Clear search functionality - - Search highlighting - -- **FilterPanel.test.tsx**: - - Category filtering - - Tag filtering with autocomplete - - Clear filters functionality - -- **Integration tests**: - - Complete search + filter + sort workflow - - URL state synchronization - - localStorage persistence - -## 7. Accessibility Requirements - -- Proper ARIA labels for all interactive elements -- Keyboard navigation support for all controls -- Screen reader announcements for filter changes -- Focus management for search and filter interactions -- High contrast support for search highlighting - -## 8. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_2_Core_UI_Implementation/Task_2.2_Tool_List_View_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_UI_Specialist) -- Task reference (Phase 2 / Task 2.2) -- Component architecture overview -- Search and filter implementation details -- Performance optimization strategies applied -- Responsive design approach -- Test coverage summary - -## 9. Clarification Instruction - -If any part of this task is unclear, please ask before proceeding. Key areas: -- Specific search algorithm preferences -- Filter UI layout preferences -- Pagination vs virtual scrolling choice -- Performance requirements for large datasets - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_2.3_Tool_Detail_View.md b/prompts/tasks/Task_2.3_Tool_Detail_View.md deleted file mode 100644 index 03673be..0000000 --- a/prompts/tasks/Task_2.3_Tool_Detail_View.md +++ /dev/null @@ -1,272 +0,0 @@ -# APM Task Assignment: Create Tool Detail View Component - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 2, Task 2.3** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Build detailed view for individual tools showing inputs, outputs, metadata, and execution interface. - -**Prerequisites:** Tasks 2.1 and 2.2 completed - Data service layer and tool list should be functional. - -## 2. Detailed Action Steps - -1. **Create ToolDetail component structure:** - - Create `src/components/tools/ToolDetail.tsx` as the main detail component: - ```typescript - interface ToolDetailProps { - toolId: string; - } - - export const ToolDetail: React.FC = ({ toolId }) => { - // Use useToolById hook from Task 2.1 - // Handle loading, error, and not-found states - // Implement responsive layout with MUI Grid - }; - ``` - - Use MUI Paper component for main content container - - Implement breadcrumb navigation showing: Home > Tools > [Tool Name] - - Add loading skeleton using MUI Skeleton components - -2. **Implement tool metadata display:** - - Create `src/components/tools/ToolHeader.tsx`: - ```typescript - interface ToolHeaderProps { - tool: Tool; - onRunClick: () => void; - } - ``` - - Display tool information in a structured layout: - - Tool name as H1 typography - - Description in body text with proper line spacing - - Category as a prominent chip/badge - - Tags as MUI Chips in a flex container - - Add action buttons: - - Primary "Run Tool" button (non-functional but styled) - - Secondary "Bookmark" button (placeholder) - - Share button with copy-to-clipboard functionality - -3. **Create inputs display section:** - - Create `src/components/tools/InputsList.tsx`: - ```typescript - interface InputsListProps { - inputs: ToolInput[]; - values?: Record; - onChange?: (values: Record) => void; - readOnly?: boolean; - } - ``` - - Display each input parameter with: - - Parameter name and human-readable label - - Data type badge (string, number, geojson, etc.) - - Required/optional indicator with clear visual distinction - - Help text or description if available - - Input field for parameter entry (for future functionality) - - Use MUI List, ListItem, ListItemIcon, ListItemText - - Add type-specific icons for different parameter types: - - TextFields icon for string inputs - - Numbers icon for numeric inputs - - Map icon for geojson inputs - -4. **Implement outputs display section:** - - Create `src/components/tools/OutputsList.tsx`: - ```typescript - interface OutputsListProps { - outputs: ToolOutput[]; - showPreview?: boolean; - } - ``` - - Display output parameters with consistent styling to inputs: - - Output name and label - - Data type badges - - Expected output description - - Placeholder preview areas for future output display - - Use similar visual design to InputsList for consistency - - Add "Expected Output" section header - -5. **Add execution interface placeholder:** - - Create `src/components/tools/ExecutionPanel.tsx`: - ```typescript - interface ExecutionPanelProps { - tool: Tool; - onExecute: (inputs: Record) => void; - isExecuting?: boolean; - } - ``` - - Design execution interface with: - - Input parameter form (using InputsList component) - - Large "Execute Tool" button with loading states - - Execution status indicators - - Output preview area (placeholder for Phase 3) - - Add form validation for required parameters - - Implement proper loading states with MUI CircularProgress - -## 3. Navigation and Action Handling - -**Navigation Components:** -```typescript -// Breadcrumb component -export const ToolBreadcrumbs: React.FC<{ toolName: string }> = ({ toolName }) => { - // Use MUI Breadcrumbs component - // Include navigation links back to tool list -}; - -// Back button functionality -export const BackButton: React.FC = () => { - // Use React Router's navigate hook - // Style as secondary button -}; -``` - -**Action Handlers:** -- Implement copy-to-clipboard for share functionality -- Add bookmark toggle state (stored in localStorage) -- Create execution handler that validates inputs and shows status messages -- Add keyboard shortcuts (Ctrl+Enter to execute, Esc to go back) - -## 4. Layout and Design Specifications - -**Responsive Layout:** -- **Desktop**: Two-column layout (inputs/outputs side-by-side) -- **Tablet**: Single column with collapsible sections -- **Mobile**: Stack all sections vertically with optimal spacing - -**Visual Hierarchy:** -```typescript -// Layout structure - - - - - - - - - - - - - - - - - - - - - - -``` - -## 5. State Management - -**Component State:** -```typescript -interface ToolDetailState { - inputValues: Record; - isBookmarked: boolean; - executionStatus: 'idle' | 'validating' | 'executing' | 'complete' | 'error'; - validationErrors: Record; -} -``` - -**Persistence:** -- Save bookmarked tools to localStorage -- Persist input parameter values during session -- Clear execution state when navigating away - -## 6. Error Handling and Edge Cases - -**Error Scenarios:** -- Tool not found (404-style error page) -- Network errors loading tool data -- Invalid tool ID format -- Missing required fields in tool data - -**Fallback UI:** -- Skeleton loading states for all major sections -- Error boundaries for component failures -- Graceful degradation when optional data is missing -- User-friendly error messages with actionable suggestions - -## 7. Expected Output & Deliverables - -**Success Criteria:** -- Tool details display correctly with all metadata -- Inputs and outputs are clearly presented with type information -- Navigation breadcrumbs work properly -- Responsive design functions on all screen sizes -- Non-functional execution interface is properly styled -- Error and loading states are handled gracefully - -**Deliverables:** -1. `components/tools/ToolDetail.tsx` - Main detail page component -2. `components/tools/ToolHeader.tsx` - Tool metadata and actions -3. `components/tools/InputsList.tsx` - Parameter input display -4. `components/tools/OutputsList.tsx` - Expected output display -5. `components/tools/ExecutionPanel.tsx` - Execution interface -6. `components/tools/ToolBreadcrumbs.tsx` - Navigation breadcrumbs -7. `utils/inputValidation.ts` - Input validation utilities -8. `hooks/useBookmarks.ts` - Bookmark management hook -9. Comprehensive test suite for all components - -## 8. Testing Requirements - -Create tests for: -- **ToolDetail.test.tsx**: - - Renders tool details correctly - - Handles not found scenarios - - Navigation functionality works - - Responsive layout behavior - -- **ToolHeader.test.tsx**: - - Displays all tool metadata - - Action buttons function properly - - Share functionality works - -- **InputsList/OutputsList.test.tsx**: - - Parameter display accuracy - - Type badge rendering - - Required/optional indicators - -- **ExecutionPanel.test.tsx**: - - Form validation logic - - Button states and loading - - Error handling - -- **Integration tests**: - - Complete tool detail workflow - - Navigation integration - - State persistence - -## 9. Accessibility Requirements - -- Proper heading hierarchy (H1 for tool name, H2 for sections) -- ARIA labels for all interactive elements -- Keyboard navigation for all actions -- Screen reader support for parameter types and requirements -- Focus management for form inputs -- High contrast support for status indicators - -## 10. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_2_Core_UI_Implementation/Task_2.3_Tool_Detail_View_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_UI_Specialist) -- Task reference (Phase 2 / Task 2.3) -- Component architecture decisions -- Layout and responsive design approach -- State management strategy -- Accessibility features implemented -- Test coverage achieved - -## 11. Clarification Instruction - -If any part of this task is unclear, please ask before proceeding. Key areas: -- Specific layout preferences for inputs/outputs -- Validation requirements for parameter inputs -- Bookmark storage mechanism preferences -- Error handling approach - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_2.4_Routing_Navigation.md b/prompts/tasks/Task_2.4_Routing_Navigation.md deleted file mode 100644 index b43fb84..0000000 --- a/prompts/tasks/Task_2.4_Routing_Navigation.md +++ /dev/null @@ -1,295 +0,0 @@ -# APM Task Assignment: Implement Routing and Navigation - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 2, Task 2.4** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Set up React Router for navigation between list and detail views with URL state management. - -**Prerequisites:** Tasks 2.1, 2.2, and 2.3 completed - Data service, tool list, and tool detail components should be functional. - -## 2. Detailed Action Steps - -1. **Install and configure React Router:** - - Install React Router DOM: - ```bash - pnpm add react-router-dom - pnpm add -D @types/react-router-dom - ``` - - Create `src/router/AppRouter.tsx` with route configuration: - ```typescript - export const AppRouter: React.FC = () => { - return ( - - - } /> - } /> - } /> - } /> - } /> - - - ); - }; - ``` - - Update `src/main.tsx` to use AppRouter instead of direct App component - - Configure basename for potential subdirectory deployment - -2. **Create page-level components:** - - Create `src/pages/HomePage.tsx`: - ```typescript - export const HomePage: React.FC = () => { - // Welcome page with navigation to tools - // Include project information and quick links - // Use existing MainLayout wrapper - }; - ``` - - Create `src/pages/ToolListPage.tsx`: - ```typescript - export const ToolListPage: React.FC = () => { - // Wrapper for ToolList component - // Handle URL query parameters for search/filters - // Include page title and meta information - }; - ``` - - Create `src/pages/ToolDetailPage.tsx`: - ```typescript - export const ToolDetailPage: React.FC = () => { - const { toolId } = useParams<{ toolId: string }>(); - // Use ToolDetail component with toolId - // Handle invalid/missing toolId - // Set document title to tool name - }; - ``` - - Create `src/pages/NotFoundPage.tsx`: - - 404 error page with navigation back to home - - Suggest searching for tools - - Use MUI error styling and icons - -3. **Implement navigation components:** - - Update `src/components/layout/NavigationDrawer.tsx`: - ```typescript - // Replace placeholder onClick handlers with React Router navigation - import { useNavigate, useLocation } from 'react-router-dom'; - - const navigate = useNavigate(); - const location = useLocation(); - - // Highlight active navigation items based on current route - // Add proper navigation handlers for each menu item - ``` - - Create `src/components/navigation/NavLink.tsx`: - ```typescript - interface NavLinkProps { - to: string; - children: React.ReactNode; - icon?: React.ReactNode; - active?: boolean; - } - ``` - - Update existing breadcrumb components to use React Router navigation - -4. **Implement URL state management:** - - Create `src/hooks/useUrlState.ts`: - ```typescript - export function useUrlState( - key: string, - defaultValue: T, - serialize?: (value: T) => string, - deserialize?: (value: string) => T - ) { - // Sync component state with URL query parameters - // Handle encoding/decoding of complex state objects - // Debounce URL updates to avoid excessive history entries - } - ``` - - Update ToolListPage to sync search/filter state with URL: - ```typescript - const [searchQuery, setSearchQuery] = useUrlState('search', ''); - const [selectedCategories, setSelectedCategories] = useUrlState('categories', []); - const [sortOption, setSortOption] = useUrlState('sort', 'name-asc'); - ``` - - Create shareable URLs for filtered tool views - - Implement deep linking for specific tool detail views - -## 3. Route Configuration and Structure - -**Route Definitions:** -```typescript -interface RouteConfig { - path: string; - element: React.ComponentType; - title?: string; - requiresAuth?: boolean; -} - -const routes: RouteConfig[] = [ - { path: '/', element: HomePage, title: 'ToolVault - Analysis Tools' }, - { path: '/tools', element: ToolListPage, title: 'Tool Catalog - ToolVault' }, - { path: '/tools/:toolId', element: ToolDetailPage }, // Dynamic title - { path: '/history', element: HistoryPage, title: 'Execution History - ToolVault' }, -]; -``` - -**URL Structure:** -- `/` - Homepage with welcome content -- `/tools` - Tool list with optional query parameters -- `/tools?search=analysis&category=text` - Filtered tool list -- `/tools/wordcount` - Individual tool detail page -- `/history` - Execution history (placeholder) - -## 4. Navigation Enhancement Features - -**Browser Integration:** -- Implement proper page titles for each route using React Helmet or document.title -- Add browser back/forward button support -- Handle page refresh on any route -- Implement scroll restoration for list views - -**Navigation Guards:** -```typescript -// Route protection hook -export const useRouteGuard = (toolId?: string) => { - const navigate = useNavigate(); - - useEffect(() => { - // Validate toolId exists in available tools - // Redirect to 404 if tool not found - // Handle loading states during validation - }, [toolId, navigate]); -}; -``` - -**Active State Management:** -- Highlight current page in navigation drawer -- Update page breadcrumbs dynamically -- Show loading states during route transitions - -## 5. Error Handling and Route Guards - -**Error Scenarios:** -- Invalid tool IDs in detail routes -- Malformed URL query parameters -- Network errors during route data loading -- Browser navigation edge cases - -**Route Validation:** -```typescript -// Tool ID validation -export const ToolDetailGuard: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const { toolId } = useParams(); - const { data: tools, isLoading } = useToolVaultData(); - - if (isLoading) return ; - if (!tools?.tools.find(t => t.id === toolId)) { - return ; - } - - return <>{children}; -}; -``` - -## 6. Performance Optimizations - -**Code Splitting:** -```typescript -// Lazy load page components -const ToolListPage = lazy(() => import('../pages/ToolListPage')); -const ToolDetailPage = lazy(() => import('../pages/ToolDetailPage')); - -// Wrap routes with Suspense -}> - - } /> - } /> - - -``` - -**Route Preloading:** -- Preload tool detail pages on list item hover -- Cache frequently accessed routes -- Implement prefetch strategies for common navigation patterns - -## 7. Expected Output & Deliverables - -**Success Criteria:** -- All routes navigate correctly without page refresh -- URL state synchronization works for search/filters -- Browser back/forward buttons function properly -- Deep linking works for all pages -- Active navigation states are correct -- 404 handling works for invalid routes -- Page titles update correctly - -**Deliverables:** -1. `router/AppRouter.tsx` - Main routing configuration -2. `pages/HomePage.tsx` - Landing page component -3. `pages/ToolListPage.tsx` - Tool list page wrapper -4. `pages/ToolDetailPage.tsx` - Tool detail page wrapper -5. `pages/NotFoundPage.tsx` - 404 error page -6. `hooks/useUrlState.ts` - URL state synchronization hook -7. `components/navigation/NavLink.tsx` - Navigation link component -8. Updated navigation components with routing -9. Comprehensive routing tests - -## 8. Testing Requirements - -Create tests for: -- **Routing Tests** (`router/AppRouter.test.tsx`): - - All routes render correctly - - Invalid routes show 404 page - - Navigation between routes works - - URL parameter parsing - -- **Page Component Tests**: - - Each page renders its content - - Page titles are set correctly - - Route parameters are handled properly - -- **Navigation Tests**: - - Active states update correctly - - Navigation drawer links work - - Breadcrumb navigation functions - -- **URL State Tests** (`hooks/useUrlState.test.ts`): - - State syncs with URL correctly - - Complex objects serialize/deserialize properly - - Debouncing works correctly - -- **Integration Tests**: - - Complete navigation workflows - - Deep linking functionality - - Browser navigation edge cases - -## 9. Accessibility Considerations - -- Proper focus management during route changes -- Screen reader announcements for page changes -- Keyboard navigation for all route links -- Skip links for main content areas -- Proper heading hierarchy maintenance across routes - -## 10. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_2_Core_UI_Implementation/Task_2.4_Routing_Navigation_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Frontend_Dev) -- Task reference (Phase 2 / Task 2.4) -- Router configuration approach -- URL state management strategy -- Performance optimizations implemented -- Error handling and validation approach -- Test coverage for routing functionality - -## 11. Clarification Instruction - -If any part of this task is unclear, please ask before proceeding. Key areas: -- Route structure preferences -- URL parameter naming conventions -- Error handling strategies -- Code splitting implementation approach - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_3.1_Unit_Testing.md b/prompts/tasks/Task_3.1_Unit_Testing.md deleted file mode 100644 index bbf8c59..0000000 --- a/prompts/tasks/Task_3.1_Unit_Testing.md +++ /dev/null @@ -1,331 +0,0 @@ -# APM Task Assignment: Write Comprehensive Unit Tests - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 3, Task 3.1** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Achieve comprehensive unit test coverage for all React components and core functionality. - -**Prerequisites:** Phase 2 completed - All core UI components should be implemented and functional. - -## 2. Detailed Action Steps - -1. **Test Data Service Layer:** - - Create comprehensive tests for `src/services/toolVaultService.ts`: - ```typescript - describe('toolVaultService', () => { - it('should fetch index.json successfully'); - it('should handle network errors gracefully'); - it('should validate data against schema'); - it('should implement retry logic for failed requests'); - }); - ``` - - Test error handling scenarios and edge cases - - Verify caching behavior and data transformation logic - - Mock fetch requests using MSW or Jest mocks - -2. **Test React Hooks:** - - Write tests for `src/hooks/useToolVaultData.ts`: - ```typescript - describe('useToolVaultData', () => { - it('should return loading state initially'); - it('should return data after successful fetch'); - it('should handle error states'); - it('should refetch data when invalidated'); - }); - ``` - - Test `useToolById` hook with valid/invalid IDs - - Test `useDebouncedValue` with various delay values - - Verify hook cleanup and memory leak prevention - -3. **Test Core UI Components:** - - **ToolList Component Tests:** - ```typescript - describe('ToolList', () => { - it('should render tool cards correctly'); - it('should handle search functionality'); - it('should filter tools by category'); - it('should sort tools by different criteria'); - it('should handle empty search results'); - it('should call onViewDetails when card is clicked'); - }); - ``` - - **ToolDetail Component Tests:** - ```typescript - describe('ToolDetail', () => { - it('should display tool information correctly'); - it('should render input parameters'); - it('should render output specifications'); - it('should handle loading states'); - it('should handle tool not found scenarios'); - it('should navigate back correctly'); - }); - ``` - - **ExecutionPanel Component Tests:** - ```typescript - describe('ExecutionPanel', () => { - it('should validate input parameters'); - it('should display execution progress'); - it('should handle execution errors'); - it('should show output results'); - it('should allow execution reset'); - }); - ``` - -4. **Test Utility Functions:** - - Test search and filter algorithms in `src/utils/searchUtils.ts` - - Test input validation functions in `src/utils/inputValidation.ts` - - Test type guards and validators - - Verify data formatting and transformation utilities - -## 3. Testing Configuration and Setup - -**Test Environment Setup:** -```typescript -// src/test-utils/setup.ts -import { render, RenderOptions } from '@testing-library/react'; -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { ThemeProvider } from '@mui/material/styles'; -import { BrowserRouter } from 'react-router-dom'; -import { theme } from '../theme/theme'; - -const AllProviders: FC<{ children: ReactNode }> = ({ children }) => { - const queryClient = new QueryClient({ - defaultOptions: { - queries: { retry: false }, - mutations: { retry: false }, - }, - }); - - return ( - - - - {children} - - - - ); -}; - -export const renderWithProviders = (ui: ReactElement, options?: RenderOptions) => { - return render(ui, { wrapper: AllProviders, ...options }); -}; -``` - -**Mock Data Factories:** -```typescript -// src/test-utils/mockData.ts -export const createMockTool = (overrides?: Partial): Tool => ({ - id: 'test-tool', - name: 'Test Tool', - description: 'A test tool for unit testing', - category: 'Testing', - tags: ['test', 'mock'], - inputs: [ - { - name: 'input1', - label: 'Test Input', - type: 'string', - required: true, - }, - ], - outputs: [ - { - name: 'output1', - label: 'Test Output', - type: 'string', - }, - ], - ...overrides, -}); -``` - -## 4. Coverage Requirements and Quality Gates - -**Coverage Targets:** -- **Statements**: 90% minimum -- **Branches**: 85% minimum -- **Functions**: 95% minimum -- **Lines**: 90% minimum - -**Quality Gates:** -```typescript -// jest.config.cjs -module.exports = { - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.d.ts', - '!src/test-utils/**/*', - '!src/main.tsx', - '!src/vite-env.d.ts', - ], - coverageThreshold: { - global: { - statements: 90, - branches: 85, - functions: 95, - lines: 90, - }, - }, -}; -``` - -## 5. Testing Patterns and Best Practices - -**Component Testing Pattern:** -```typescript -describe('ComponentName', () => { - const defaultProps = { - // minimal required props - }; - - const renderComponent = (props = {}) => { - return renderWithProviders( - - ); - }; - - beforeEach(() => { - // Reset mocks and state - }); - - describe('rendering', () => { - it('should render without crashing'); - it('should display required content'); - }); - - describe('user interactions', () => { - it('should handle click events'); - it('should update state correctly'); - }); - - describe('error handling', () => { - it('should handle error props gracefully'); - it('should display error messages'); - }); -}); -``` - -**Async Testing Pattern:** -```typescript -it('should handle async operations', async () => { - const { getByText, findByText } = renderComponent(); - - fireEvent.click(getByText('Load Data')); - - expect(getByText('Loading...')).toBeInTheDocument(); - - await findByText('Data loaded successfully'); - - expect(queryByText('Loading...')).not.toBeInTheDocument(); -}); -``` - -## 6. Integration with CI/CD - -**GitHub Actions Integration:** -```yaml -# .github/workflows/test.yml -- name: Run unit tests with coverage - run: pnpm test:coverage - -- name: Upload coverage reports - uses: codecov/codecov-action@v4 - with: - file: ./coverage/lcov.info - flags: unittests - name: codecov-umbrella - fail_ci_if_error: true -``` - -## 7. Expected Output & Deliverables - -**Success Criteria:** -- All components have comprehensive test coverage -- Test suite runs in under 30 seconds -- 100% test pass rate in CI/CD pipeline -- Coverage thresholds met across all metrics -- No flaky or inconsistent tests - -**Deliverables:** -1. **Component Tests:** - - `src/components/tools/__tests__/ToolList.test.tsx` - - `src/components/tools/__tests__/ToolDetail.test.tsx` - - `src/components/tools/__tests__/ToolCard.test.tsx` - - `src/components/tools/__tests__/ExecutionPanel.test.tsx` - - `src/components/layout/__tests__/NavigationDrawer.test.tsx` - -2. **Hook Tests:** - - `src/hooks/__tests__/useToolVaultData.test.ts` - - `src/hooks/__tests__/useToolById.test.ts` - - `src/hooks/__tests__/useDebouncedValue.test.ts` - -3. **Service Tests:** - - `src/services/__tests__/toolVaultService.test.ts` - -4. **Utility Tests:** - - `src/utils/__tests__/searchUtils.test.ts` - - `src/utils/__tests__/inputValidation.test.ts` - - `src/utils/__tests__/validators.test.ts` - -5. **Test Infrastructure:** - - Enhanced test utilities and setup - - Mock data factories and generators - - Coverage reporting configuration - -## 8. Performance and Accessibility Testing - -**Performance Testing:** -```typescript -describe('Performance', () => { - it('should render large tool lists efficiently', () => { - const manyTools = Array.from({ length: 1000 }, (_, i) => - createMockTool({ id: `tool-${i}` }) - ); - - const startTime = performance.now(); - renderWithProviders(); - const endTime = performance.now(); - - expect(endTime - startTime).toBeLessThan(100); // 100ms threshold - }); -}); -``` - -**Accessibility Testing:** -```typescript -import { axe, toHaveNoViolations } from 'jest-axe'; - -expect.extend(toHaveNoViolations); - -it('should not have accessibility violations', async () => { - const { container } = renderComponent(); - const results = await axe(container); - expect(results).toHaveNoViolations(); -}); -``` - -## 9. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_3_Testing_Polish/Task_3.1_Unit_Testing_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_QA_Specialist) -- Task reference (Phase 3 / Task 3.1) -- Testing strategy and approach -- Coverage metrics achieved -- Performance test results -- Accessibility compliance status -- CI/CD integration details - -## 10. Clarification Instructions - -If any part of this task is unclear, please ask before proceeding. Key areas: -- Specific coverage threshold requirements -- Testing framework preferences (Jest vs Vitest) -- Mock strategy for external dependencies -- Performance testing requirements - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_3.2_E2E_Testing.md b/prompts/tasks/Task_3.2_E2E_Testing.md deleted file mode 100644 index 25db940..0000000 --- a/prompts/tasks/Task_3.2_E2E_Testing.md +++ /dev/null @@ -1,418 +0,0 @@ -# APM Task Assignment: Implement End-to-End Testing - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 3, Task 3.2** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Create end-to-end tests covering critical user workflows using Playwright. - -**Prerequisites:** Task 3.1 completed - Unit tests should be implemented and passing. - -## 2. Detailed Action Steps - -1. **Set up E2E Test Infrastructure:** - - Configure Playwright for multiple browser testing: - ```typescript - // playwright.config.ts - export default defineConfig({ - testDir: './e2e', - fullyParallel: true, - forbidOnly: !!process.env.CI, - retries: process.env.CI ? 2 : 0, - workers: process.env.CI ? 1 : undefined, - reporter: 'html', - use: { - baseURL: 'http://localhost:5173', - trace: 'on-first-retry', - screenshot: 'only-on-failure', - }, - projects: [ - { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, - { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, - { name: 'webkit', use: { ...devices['Desktop Safari'] } }, - { name: 'mobile', use: { ...devices['iPhone 13'] } }, - ], - webServer: { - command: 'pnpm dev', - url: 'http://localhost:5173', - reuseExistingServer: !process.env.CI, - }, - }); - ``` - - Create page object models for maintainable tests - - Set up test data fixtures and helpers - - Configure visual regression testing - -2. **Create Page Object Models:** - - Create `e2e/pages/HomePage.ts`: - ```typescript - export class HomePage { - constructor(private page: Page) {} - - async goto() { - await this.page.goto('/'); - } - - async navigateToTools() { - await this.page.click('[data-testid="browse-tools-btn"]'); - } - - async expectWelcomeMessage() { - await expect(this.page.locator('h2')).toContainText('Welcome to ToolVault'); - } - } - ``` - - Create `e2e/pages/ToolListPage.ts`: - ```typescript - export class ToolListPage { - constructor(private page: Page) {} - - async searchTools(query: string) { - await this.page.fill('[data-testid="search-input"]', query); - } - - async selectCategory(category: string) { - await this.page.selectOption('[data-testid="category-filter"]', category); - } - - async clickToolCard(toolId: string) { - await this.page.click(`[data-testid="tool-card-${toolId}"]`); - } - - async expectToolsVisible(count: number) { - await expect(this.page.locator('[data-testid^="tool-card-"]')).toHaveCount(count); - } - } - ``` - - Create `e2e/pages/ToolDetailPage.ts` for tool detail interactions - -3. **Test Core User Journeys:** - - **Homepage to Tool List Journey:** - ```typescript - test('should navigate from homepage to tool list', async ({ page }) => { - const homePage = new HomePage(page); - const toolListPage = new ToolListPage(page); - - await homePage.goto(); - await homePage.expectWelcomeMessage(); - await homePage.navigateToTools(); - - await expect(page).toHaveURL('/tools'); - await toolListPage.expectToolsVisible(0); // Wait for loading - }); - ``` - - **Tool Search and Filter Workflow:** - ```typescript - test('should search and filter tools', async ({ page }) => { - const toolListPage = new ToolListPage(page); - - await page.goto('/tools'); - await toolListPage.searchTools('analysis'); - await toolListPage.expectToolsVisible(2); - - await toolListPage.selectCategory('text'); - await toolListPage.expectToolsVisible(1); - }); - ``` - - **Tool Detail View Navigation:** - ```typescript - test('should navigate to tool details and back', async ({ page }) => { - const toolListPage = new ToolListPage(page); - const toolDetailPage = new ToolDetailPage(page); - - await page.goto('/tools'); - await toolListPage.clickToolCard('wordcount'); - - await expect(page).toHaveURL('/tools/wordcount'); - await toolDetailPage.expectToolHeader('Word Count Tool'); - - await toolDetailPage.clickBackButton(); - await expect(page).toHaveURL('/tools'); - }); - ``` - -4. **Test Error Scenarios:** - - **Network Error Handling:** - ```typescript - test('should handle network errors gracefully', async ({ page }) => { - await page.route('**/data/index.json', route => - route.abort('internetdisconnected') - ); - - await page.goto('/tools'); - await expect(page.locator('[data-testid="error-message"]')) - .toBeVisible(); - await expect(page.locator('[data-testid="retry-button"]')) - .toBeVisible(); - }); - ``` - - **Invalid Tool ID Navigation:** - ```typescript - test('should show 404 for invalid tool ID', async ({ page }) => { - await page.goto('/tools/invalid-tool-id'); - await expect(page).toHaveURL('/404'); - await expect(page.locator('h1')).toContainText('404'); - }); - ``` - - **Empty Search Results:** - ```typescript - test('should handle empty search results', async ({ page }) => { - const toolListPage = new ToolListPage(page); - - await page.goto('/tools'); - await toolListPage.searchTools('nonexistent-tool-xyz'); - - await expect(page.locator('[data-testid="no-results"]')) - .toContainText('No tools match your search'); - }); - ``` - -## 3. Visual Regression Testing - -**Screenshot Comparisons:** -```typescript -test('should match homepage screenshot', async ({ page }) => { - await page.goto('/'); - await expect(page).toHaveScreenshot('homepage.png'); -}); - -test('should match tool list layout', async ({ page }) => { - await page.goto('/tools'); - await page.waitForSelector('[data-testid^="tool-card-"]'); - await expect(page).toHaveScreenshot('tool-list.png'); -}); - -test('should match tool detail layout', async ({ page }) => { - await page.goto('/tools/wordcount'); - await page.waitForSelector('[data-testid="tool-header"]'); - await expect(page).toHaveScreenshot('tool-detail.png'); -}); -``` - -**Responsive Layout Testing:** -```typescript -test.describe('Responsive Design', () => { - const viewports = [ - { name: 'mobile', width: 375, height: 667 }, - { name: 'tablet', width: 768, height: 1024 }, - { name: 'desktop', width: 1920, height: 1080 }, - ]; - - viewports.forEach(({ name, width, height }) => { - test(`should render correctly on ${name}`, async ({ page }) => { - await page.setViewportSize({ width, height }); - await page.goto('/tools'); - await expect(page).toHaveScreenshot(`tool-list-${name}.png`); - }); - }); -}); -``` - -## 4. Performance Testing - -**Page Load Performance:** -```typescript -test('should load homepage within performance budget', async ({ page }) => { - const startTime = Date.now(); - await page.goto('/'); - await page.waitForLoadState('networkidle'); - const loadTime = Date.now() - startTime; - - expect(loadTime).toBeLessThan(3000); // 3 second budget -}); - -test('should load tool list efficiently', async ({ page }) => { - await page.goto('/tools'); - - // Measure Time to Interactive - const tti = await page.evaluate(() => { - return new Promise(resolve => { - new PerformanceObserver((list) => { - const entries = list.getEntries(); - resolve(entries[entries.length - 1]?.startTime || 0); - }).observe({ entryTypes: ['measure'] }); - - performance.measure('tti-measure'); - }); - }); - - expect(tti).toBeLessThan(2000); // 2 second TTI budget -}); -``` - -## 5. Accessibility Testing - -**Keyboard Navigation:** -```typescript -test('should support keyboard navigation', async ({ page }) => { - await page.goto('/'); - - // Tab through interactive elements - await page.keyboard.press('Tab'); - await expect(page.locator(':focus')).toHaveAttribute('data-testid', 'browse-tools-btn'); - - await page.keyboard.press('Enter'); - await expect(page).toHaveURL('/tools'); -}); - -test('should have proper ARIA labels', async ({ page }) => { - await page.goto('/tools'); - - await expect(page.locator('[data-testid="search-input"]')) - .toHaveAttribute('aria-label'); - await expect(page.locator('[data-testid="category-filter"]')) - .toHaveAttribute('aria-label'); -}); -``` - -**Screen Reader Compatibility:** -```typescript -test('should announce page changes', async ({ page }) => { - await page.goto('/'); - - // Check for live region updates - await page.click('[data-testid="browse-tools-btn"]'); - - await expect(page.locator('[aria-live="polite"]')) - .toContainText('Tool catalog loaded'); -}); -``` - -## 6. Cross-Browser Testing - -**Browser-Specific Tests:** -```typescript -test.describe('Cross-Browser Compatibility', () => { - ['chromium', 'firefox', 'webkit'].forEach(browserName => { - test(`should work correctly in ${browserName}`, async ({ page }) => { - await page.goto('/tools'); - await page.click('[data-testid^="tool-card-"]:first-child'); - - // Verify core functionality works - await expect(page.locator('[data-testid="tool-header"]')).toBeVisible(); - await expect(page.locator('[data-testid="input-section"]')).toBeVisible(); - await expect(page.locator('[data-testid="output-section"]')).toBeVisible(); - }); - }); -}); -``` - -## 7. Test Data Management - -**Fixtures and Test Data:** -```typescript -// e2e/fixtures/tools.json -{ - "sampleTools": [ - { - "id": "wordcount", - "name": "Word Count Tool", - "description": "Count words in text", - "category": "text", - "tags": ["analysis", "text"], - "inputs": [ - { - "name": "text", - "label": "Input Text", - "type": "string", - "required": true - } - ], - "outputs": [ - { - "name": "count", - "label": "Word Count", - "type": "number" - } - ] - } - ] -} - -// e2e/helpers/mockData.ts -export const setupMockData = async (page: Page) => { - await page.route('**/data/index.json', async route => { - const tools = await import('../fixtures/tools.json'); - await route.fulfill({ - status: 200, - contentType: 'application/json', - body: JSON.stringify({ - name: "Test Tool Collection", - version: "1.0.0", - tools: tools.sampleTools - }) - }); - }); -}; -``` - -## 8. CI/CD Integration - -**GitHub Actions Workflow:** -```yaml -# .github/workflows/e2e.yml (update existing) -- name: Run E2E tests - run: pnpm test:e2e - -- name: Upload Playwright report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 - -- name: Upload test results - uses: actions/upload-artifact@v4 - if: always() - with: - name: e2e-test-results - path: test-results/ - retention-days: 30 -``` - -## 9. Expected Output & Deliverables - -**Success Criteria:** -- All critical user journeys covered by E2E tests -- Visual regression tests prevent UI breaking changes -- Performance budgets enforced in CI/CD -- Cross-browser compatibility verified -- Accessibility requirements validated -- Test reports generated automatically - -**Deliverables:** -1. **Test Files:** - - `e2e/homepage.spec.ts` - Homepage functionality tests - - `e2e/tool-list.spec.ts` - Tool catalog and filtering tests - - `e2e/tool-detail.spec.ts` - Tool detail view tests - - `e2e/navigation.spec.ts` - Routing and navigation tests - - `e2e/error-handling.spec.ts` - Error scenario tests - -2. **Page Object Models:** - - `e2e/pages/HomePage.ts` - - `e2e/pages/ToolListPage.ts` - - `e2e/pages/ToolDetailPage.ts` - - `e2e/pages/NotFoundPage.ts` - -3. **Test Infrastructure:** - - `e2e/fixtures/` - Test data fixtures - - `e2e/helpers/` - Test helper utilities - - Updated Playwright configuration - - Visual regression baseline images - -## 10. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_3_Testing_Polish/Task_3.2_E2E_Testing_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_QA_Specialist) -- Task reference (Phase 3 / Task 3.2) -- E2E testing strategy and coverage -- Performance testing results -- Cross-browser compatibility status -- Visual regression testing approach -- CI/CD integration details - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_3.3_Performance_Optimization.md b/prompts/tasks/Task_3.3_Performance_Optimization.md deleted file mode 100644 index 898659a..0000000 --- a/prompts/tasks/Task_3.3_Performance_Optimization.md +++ /dev/null @@ -1,472 +0,0 @@ -# APM Task Assignment: Optimize Application Performance - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 3, Task 3.3** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Ensure optimal loading times and runtime performance through code splitting, bundle optimization, and performance monitoring. - -**Prerequisites:** Tasks 3.1 and 3.2 completed - Testing infrastructure should be in place. - -## 2. Detailed Action Steps - -1. **Implement Code Splitting and Lazy Loading:** - - Configure dynamic imports for route-based code splitting: - ```typescript - // src/router/AppRouter.tsx - import { lazy, Suspense } from 'react'; - - const HomePage = lazy(() => import('../pages/HomePage')); - const ToolListPage = lazy(() => import('../pages/ToolListPage')); - const ToolDetailPage = lazy(() => import('../pages/ToolDetailPage')); - - export function AppRouter() { - return ( - - }> - - } /> - } /> - } /> - - - - ); - } - ``` - - Create loading skeleton components: - ```typescript - // src/components/common/PageSkeleton.tsx - export function PageSkeleton() { - return ( - - - - - - - {[1, 2, 3, 4, 5, 6].map(i => ( - - ))} - - - - ); - } - ``` - - Implement progressive loading for heavy components - -2. **Optimize Bundle Size:** - - Configure Vite for optimal chunking: - ```typescript - // vite.config.ts - export default defineConfig({ - build: { - rollupOptions: { - output: { - manualChunks: { - vendor: ['react', 'react-dom'], - mui: ['@mui/material', '@mui/icons-material'], - router: ['react-router-dom'], - query: ['@tanstack/react-query'], - }, - }, - }, - chunkSizeWarningLimit: 1000, - }, - }); - ``` - - Analyze bundle composition: - ```bash - # Add to package.json scripts - "analyze": "npx vite-bundle-analyzer dist" - ``` - - Remove unused code and dependencies - - Optimize imports to reduce bundle size: - ```typescript - // Instead of: import { Button } from '@mui/material'; - import Button from '@mui/material/Button'; - ``` - -3. **Optimize Rendering Performance:** - - Implement React.memo for expensive components: - ```typescript - // src/components/tools/ToolCard.tsx - export const ToolCard = React.memo(({ tool, onViewDetails }) => { - // Component implementation - }); - ``` - - Add useMemo for complex calculations: - ```typescript - // src/components/tools/ToolList.tsx - const filteredAndSortedTools = useMemo(() => { - const filtered = filterTools(tools, filters); - return sortTools(filtered, sortOption); - }, [tools, filters, sortOption]); - ``` - - Optimize re-render patterns with useCallback: - ```typescript - const handleViewDetails = useCallback((tool: Tool) => { - navigate(`/tools/${tool.id}`); - }, [navigate]); - ``` - - Implement virtual scrolling for large lists: - ```typescript - // src/components/common/VirtualizedList.tsx - import { FixedSizeList as List } from 'react-window'; - - export function VirtualizedToolList({ tools }: { tools: Tool[] }) { - const Row = ({ index, style }: { index: number; style: CSSProperties }) => ( -
- -
- ); - - return ( - - {Row} - - ); - } - ``` - -4. **Optimize Asset Loading:** - - Implement image optimization: - ```typescript - // src/components/common/OptimizedImage.tsx - export function OptimizedImage({ - src, - alt, - width, - height, - loading = 'lazy' - }: OptimizedImageProps) { - return ( - - - - {alt} - - ); - } - ``` - - Configure proper caching headers: - ```typescript - // vite.config.ts - export default defineConfig({ - build: { - assetsDir: 'assets', - rollupOptions: { - output: { - assetFileNames: 'assets/[name]-[hash][extname]', - chunkFileNames: 'assets/[name]-[hash].js', - entryFileNames: 'assets/[name]-[hash].js', - }, - }, - }, - }); - ``` - - Optimize font loading: - ```html - - - - - ``` - -## 3. Performance Monitoring Implementation - -**Web Vitals Tracking:** -```typescript -// src/utils/performance.ts -import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'; - -function sendToAnalytics(metric: Metric) { - // Send to your analytics service - console.log('Performance metric:', metric); -} - -export function initPerformanceMonitoring() { - getCLS(sendToAnalytics); - getFID(sendToAnalytics); - getFCP(sendToAnalytics); - getLCP(sendToAnalytics); - getTTFB(sendToAnalytics); -} - -// Custom performance marks -export function markPerformance(name: string) { - performance.mark(`${name}-start`); - return () => { - performance.mark(`${name}-end`); - performance.measure(name, `${name}-start`, `${name}-end`); - }; -} -``` - -**Performance Budget Configuration:** -```typescript -// src/utils/performanceBudget.ts -export const PERFORMANCE_BUDGETS = { - // Time budgets (milliseconds) - firstContentfulPaint: 1800, - largestContentfulPaint: 2500, - timeToInteractive: 3000, - firstInputDelay: 100, - cumulativeLayoutShift: 0.1, - - // Size budgets (bytes) - totalBundleSize: 1000000, // 1MB - mainChunkSize: 500000, // 500KB - cssSize: 100000, // 100KB - imageSize: 200000, // 200KB per image -} as const; - -export function checkPerformanceBudget() { - // Implementation for CI/CD performance checking -} -``` - -**Performance Testing:** -```typescript -// src/__tests__/performance.test.ts -describe('Performance Tests', () => { - it('should render ToolList within performance budget', () => { - const startTime = performance.now(); - const tools = generateManyTools(1000); - - render(); - - const endTime = performance.now(); - const renderTime = endTime - startTime; - - expect(renderTime).toBeLessThan(100); // 100ms budget - }); - - it('should handle large datasets efficiently', () => { - const tools = generateManyTools(10000); - const filtered = filterTools(tools, { query: 'test' }); - - expect(filtered.length).toBeGreaterThan(0); - expect(performance.now()).toBeLessThan(50); // Fast filtering - }); -}); -``` - -## 4. Memory Management and Leak Prevention - -**Memory Leak Prevention:** -```typescript -// src/hooks/useCleanupEffect.ts -export function useCleanupEffect(effect: () => (() => void) | void, deps: DependencyList) { - useEffect(() => { - const cleanup = effect(); - return () => { - if (cleanup) cleanup(); - }; - }, deps); -} - -// Usage in components -const ToolList = () => { - useCleanupEffect(() => { - const interval = setInterval(() => { - // Some periodic task - }, 1000); - - return () => clearInterval(interval); - }, []); -}; -``` - -**Memory Usage Monitoring:** -```typescript -// src/utils/memoryMonitoring.ts -export function monitorMemoryUsage() { - if ('memory' in performance) { - const memInfo = (performance as any).memory; - console.log({ - usedJSHeapSize: memInfo.usedJSHeapSize, - totalJSHeapSize: memInfo.totalJSHeapSize, - jsHeapSizeLimit: memInfo.jsHeapSizeLimit, - }); - } -} - -// Component memory profiling -export function withMemoryProfiler( - Component: React.ComponentType -): React.ComponentType { - return (props: T) => { - useEffect(() => { - const componentName = Component.displayName || Component.name; - console.log(`${componentName} mounted`); - monitorMemoryUsage(); - - return () => { - console.log(`${componentName} unmounted`); - monitorMemoryUsage(); - }; - }, []); - - return ; - }; -} -``` - -## 5. Network Optimization - -**Request Optimization:** -```typescript -// src/services/optimizedFetch.ts -interface CacheConfig { - ttl: number; - key: string; -} - -const cache = new Map(); - -export async function fetchWithCache( - url: string, - config: CacheConfig -): Promise { - const cached = cache.get(config.key); - const now = Date.now(); - - if (cached && (now - cached.timestamp) < cached.ttl) { - return cached.data; - } - - const data = await fetch(url).then(r => r.json()); - cache.set(config.key, { data, timestamp: now, ttl: config.ttl }); - - return data; -} - -// Preloading strategies -export function preloadRoute(routePath: string) { - const link = document.createElement('link'); - link.rel = 'modulepreload'; - link.href = routePath; - document.head.appendChild(link); -} -``` - -## 6. Performance Testing Integration - -**Lighthouse CI Configuration:** -```javascript -// lighthouserc.js -module.exports = { - ci: { - collect: { - url: ['http://localhost:5173/', 'http://localhost:5173/tools'], - startServerCommand: 'pnpm preview', - numberOfRuns: 3, - }, - assert: { - assertions: { - 'categories:performance': ['error', { minScore: 0.9 }], - 'categories:accessibility': ['error', { minScore: 0.9 }], - 'categories:best-practices': ['error', { minScore: 0.9 }], - 'categories:seo': ['error', { minScore: 0.8 }], - }, - }, - upload: { - target: 'temporary-public-storage', - }, - }, -}; -``` - -**Performance CI/CD Integration:** -```yaml -# Add to .github/workflows/ci.yml -- name: Run Lighthouse CI - run: | - npm install -g @lhci/cli@0.12.x - lhci autorun - -- name: Check bundle size - run: | - npm install -g bundlesize - bundlesize - -# package.json bundlesize config -"bundlesize": [ - { - "path": "./dist/assets/*.js", - "maxSize": "500kb", - "compression": "gzip" - }, - { - "path": "./dist/assets/*.css", - "maxSize": "100kb", - "compression": "gzip" - } -] -``` - -## 7. Expected Output & Deliverables - -**Performance Targets:** -- **First Contentful Paint**: < 1.8s -- **Largest Contentful Paint**: < 2.5s -- **Time to Interactive**: < 3.0s -- **Cumulative Layout Shift**: < 0.1 -- **First Input Delay**: < 100ms -- **Bundle Size**: < 1MB total, < 500KB main chunk - -**Success Criteria:** -- All performance budgets met in CI/CD -- Bundle size optimized with proper code splitting -- Memory leaks eliminated -- Virtual scrolling implemented for large lists -- Performance monitoring integrated - -**Deliverables:** -1. **Performance Infrastructure:** - - `src/utils/performance.ts` - Performance monitoring - - `src/utils/performanceBudget.ts` - Budget configuration - - `src/components/common/PageSkeleton.tsx` - Loading states - - `src/components/common/VirtualizedList.tsx` - Virtual scrolling - -2. **Optimization Implementations:** - - Route-based code splitting - - Component memoization and optimization - - Bundle size optimization configuration - - Asset loading optimization - -3. **Monitoring and Testing:** - - Performance test suite - - Lighthouse CI integration - - Bundle size monitoring - - Memory leak prevention utilities - -## 8. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_3_Testing_Polish/Task_3.3_Performance_Optimization_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Performance_Optimizer) -- Task reference (Phase 3 / Task 3.3) -- Performance optimization strategies implemented -- Benchmark results before and after optimization -- Bundle size reduction achieved -- Performance monitoring setup details -- CI/CD performance gate configuration - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_3.4_Error_Handling.md b/prompts/tasks/Task_3.4_Error_Handling.md deleted file mode 100644 index eac20ad..0000000 --- a/prompts/tasks/Task_3.4_Error_Handling.md +++ /dev/null @@ -1,551 +0,0 @@ -# APM Task Assignment: Implement Error Handling and Loading States - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 3, Task 3.4** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Implement comprehensive error handling and user feedback systems with loading states and graceful degradation. - -**Prerequisites:** Tasks 3.1, 3.2, and 3.3 completed - Testing infrastructure and performance optimizations should be in place. - -## 2. Detailed Action Steps - -1. **Create Global Error Boundary System:** - - Implement root error boundary: - ```typescript - // src/components/common/ErrorBoundary.tsx - export class ErrorBoundary extends React.Component< - { children: ReactNode; fallback?: ComponentType<{ error: Error }> }, - { hasError: boolean; error: Error | null } - > { - constructor(props: any) { - super(props); - this.state = { hasError: false, error: null }; - } - - static getDerivedStateFromError(error: Error) { - return { hasError: true, error }; - } - - componentDidCatch(error: Error, errorInfo: ErrorInfo) { - console.error('Error boundary caught an error:', error, errorInfo); - // Send to error reporting service - this.reportError(error, errorInfo); - } - - reportError = (error: Error, errorInfo: ErrorInfo) => { - // Implement error reporting to analytics service - const errorReport = { - message: error.message, - stack: error.stack, - componentStack: errorInfo.componentStack, - timestamp: new Date().toISOString(), - }; - console.error('Error Report:', errorReport); - }; - - render() { - if (this.state.hasError) { - const FallbackComponent = this.props.fallback || ErrorFallback; - return ; - } - - return this.props.children; - } - } - ``` - - Create route-specific error boundaries for better error isolation - - Implement error recovery mechanisms - -2. **Build Error Display Components:** - - Create comprehensive error fallback component: - ```typescript - // src/components/common/ErrorFallback.tsx - export function ErrorFallback({ error }: { error: Error }) { - return ( - - - - - Something went wrong - - - We encountered an unexpected error. Please try refreshing the page. - - - - - - {process.env.NODE_ENV === 'development' && ( - - }> - Error Details - - - - {error.stack} - - - - )} - - - ); - } - ``` - - Create network error component: - ```typescript - // src/components/common/NetworkError.tsx - export function NetworkError({ onRetry }: { onRetry: () => void }) { - return ( - - Retry - - } - > - Unable to load data. Please check your connection and try again. - - ); - } - ``` - - Create inline error messages for form validation - -3. **Implement Loading State System:** - - Create skeleton screen components: - ```typescript - // src/components/common/ToolListSkeleton.tsx - export function ToolListSkeleton() { - return ( - - - - - - - - {Array.from({ length: 6 }).map((_, index) => ( - - - - - - - - - - - - - ))} - - - ); - } - ``` - - Create progress indicators for data fetching: - ```typescript - // src/components/common/LoadingSpinner.tsx - export function LoadingSpinner({ - size = 40, - message = 'Loading...', - fullScreen = false - }: LoadingSpinnerProps) { - const content = ( - - - - {message} - - - ); - - if (fullScreen) { - return ( - theme.zIndex.drawer + 1 }}> - {content} - - ); - } - - return content; - } - ``` - - Implement progressive loading with suspense boundaries - -4. **Create Notification System:** - - Build toast notification system: - ```typescript - // src/components/common/NotificationProvider.tsx - interface NotificationContextType { - showNotification: (message: string, type: 'success' | 'error' | 'info' | 'warning') => void; - } - - export const NotificationContext = createContext(null); - - export function NotificationProvider({ children }: { children: ReactNode }) { - const [notification, setNotification] = useState<{ - message: string; - type: 'success' | 'error' | 'info' | 'warning'; - } | null>(null); - - const showNotification = useCallback((message: string, type: 'success' | 'error' | 'info' | 'warning') => { - setNotification({ message, type }); - }, []); - - const handleClose = () => { - setNotification(null); - }; - - return ( - - {children} - - - {notification?.message} - - - - ); - } - - export const useNotification = () => { - const context = useContext(NotificationContext); - if (!context) { - throw new Error('useNotification must be used within NotificationProvider'); - } - return context; - }; - ``` - - Add inline error messages for forms - - Create success confirmations for user actions - -## 3. Enhanced Data Service Error Handling - -**Robust Error Handling in Services:** -```typescript -// src/services/toolVaultService.ts - Enhanced error handling -export interface ServiceError { - type: 'network' | 'validation' | 'not_found' | 'server' | 'unknown'; - message: string; - code?: string; - retryable: boolean; -} - -export class ToolVaultServiceError extends Error { - constructor( - message: string, - public readonly serviceError: ServiceError - ) { - super(message); - this.name = 'ToolVaultServiceError'; - } -} - -export const fetchToolVaultIndex = async (retries = 3): Promise => { - for (let attempt = 0; attempt <= retries; attempt++) { - try { - const response = await fetch('/data/index.json'); - - if (!response.ok) { - throw new ToolVaultServiceError( - `HTTP ${response.status}: ${response.statusText}`, - { - type: response.status === 404 ? 'not_found' : 'server', - message: `Failed to fetch tool data (${response.status})`, - code: response.status.toString(), - retryable: response.status >= 500 && response.status < 600, - } - ); - } - - const data = await response.json(); - - // Validate data structure - if (!isValidToolVaultIndex(data)) { - throw new ToolVaultServiceError( - 'Invalid data structure received', - { - type: 'validation', - message: 'The received data does not match the expected format', - retryable: false, - } - ); - } - - return data; - } catch (error) { - if (error instanceof ToolVaultServiceError) { - if (!error.serviceError.retryable || attempt === retries) { - throw error; - } - } else { - // Network or other errors - if (attempt === retries) { - throw new ToolVaultServiceError( - 'Failed to load tool data after multiple attempts', - { - type: 'network', - message: 'Unable to connect to the server. Please check your internet connection.', - retryable: true, - } - ); - } - } - - // Exponential backoff delay - await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); - } - } - - // This should never be reached, but TypeScript requires it - throw new ToolVaultServiceError( - 'Unexpected error in retry logic', - { - type: 'unknown', - message: 'An unexpected error occurred', - retryable: false, - } - ); -}; -``` - -## 4. Offline Detection and Fallbacks - -**Offline Capability:** -```typescript -// src/hooks/useNetworkStatus.ts -export function useNetworkStatus() { - const [isOnline, setIsOnline] = useState(navigator.onLine); - const [wasOffline, setWasOffline] = useState(false); - - useEffect(() => { - const handleOnline = () => { - setIsOnline(true); - if (wasOffline) { - setWasOffline(false); - // Show reconnection notification - } - }; - - const handleOffline = () => { - setIsOnline(false); - setWasOffline(true); - }; - - window.addEventListener('online', handleOnline); - window.addEventListener('offline', handleOffline); - - return () => { - window.removeEventListener('online', handleOnline); - window.removeEventListener('offline', handleOffline); - }; - }, [wasOffline]); - - return { isOnline, wasOffline }; -} - -// src/components/common/OfflineBanner.tsx -export function OfflineBanner() { - const { isOnline } = useNetworkStatus(); - - if (isOnline) return null; - - return ( - - You're offline - Some features may not be available. We'll restore full functionality when your connection returns. - - ); -} -``` - -## 5. Form Validation and Error States - -**Input Validation System:** -```typescript -// src/utils/validation.ts -export interface ValidationResult { - isValid: boolean; - errors: Record; -} - -export function validateToolInput(input: ToolInput, value: any): ValidationResult { - const errors: Record = {}; - - if (input.required && (value === undefined || value === null || value === '')) { - errors[input.name] = `${input.label} is required`; - } - - if (value !== undefined && value !== null && value !== '') { - switch (input.type) { - case 'number': - if (isNaN(Number(value))) { - errors[input.name] = `${input.label} must be a valid number`; - } else { - const num = Number(value); - if (input.minimum !== undefined && num < input.minimum) { - errors[input.name] = `${input.label} must be at least ${input.minimum}`; - } - if (input.maximum !== undefined && num > input.maximum) { - errors[input.name] = `${input.label} must be at most ${input.maximum}`; - } - } - break; - case 'string': - if (input.minLength !== undefined && value.length < input.minLength) { - errors[input.name] = `${input.label} must be at least ${input.minLength} characters`; - } - if (input.maxLength !== undefined && value.length > input.maxLength) { - errors[input.name] = `${input.label} must be at most ${input.maxLength} characters`; - } - break; - } - } - - return { - isValid: Object.keys(errors).length === 0, - errors - }; -} -``` - -## 6. Error Recovery and Retry Mechanisms - -**Smart Retry System:** -```typescript -// src/hooks/useRetry.ts -export function useRetry( - asyncFn: () => Promise, - options: { - maxAttempts?: number; - delay?: number; - backoff?: 'linear' | 'exponential'; - } = {} -) { - const { maxAttempts = 3, delay = 1000, backoff = 'exponential' } = options; - const [attempts, setAttempts] = useState(0); - const [isRetrying, setIsRetrying] = useState(false); - - const retry = useCallback(async () => { - if (attempts >= maxAttempts) { - throw new Error(`Max retry attempts (${maxAttempts}) exceeded`); - } - - setIsRetrying(true); - setAttempts(prev => prev + 1); - - try { - const result = await asyncFn(); - setAttempts(0); - return result; - } catch (error) { - if (attempts + 1 < maxAttempts) { - const retryDelay = backoff === 'exponential' - ? delay * Math.pow(2, attempts) - : delay; - - await new Promise(resolve => setTimeout(resolve, retryDelay)); - return retry(); - } - throw error; - } finally { - setIsRetrying(false); - } - }, [asyncFn, attempts, maxAttempts, delay, backoff]); - - return { retry, attempts, isRetrying, canRetry: attempts < maxAttempts }; -} -``` - -## 7. Expected Output & Deliverables - -**Success Criteria:** -- Graceful handling of all error scenarios -- Comprehensive loading states throughout the application -- User-friendly error messages with recovery options -- Offline detection and appropriate fallbacks -- Form validation with clear error feedback -- No unhandled promise rejections or console errors - -**Deliverables:** -1. **Error Handling Components:** - - `src/components/common/ErrorBoundary.tsx` - - `src/components/common/ErrorFallback.tsx` - - `src/components/common/NetworkError.tsx` - - `src/components/common/NotificationProvider.tsx` - -2. **Loading State Components:** - - `src/components/common/ToolListSkeleton.tsx` - - `src/components/common/ToolDetailSkeleton.tsx` - - `src/components/common/LoadingSpinner.tsx` - - `src/components/common/PageSkeleton.tsx` - -3. **Utility Functions and Hooks:** - - `src/utils/validation.ts` - - `src/hooks/useNetworkStatus.ts` - - `src/hooks/useRetry.ts` - - Enhanced `src/services/toolVaultService.ts` - -4. **UI Enhancements:** - - `src/components/common/OfflineBanner.tsx` - - Updated form components with validation states - - Enhanced error boundaries in routing - -## 8. Integration Requirements - -**Update Main App Structure:** -```typescript -// src/main.tsx -ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - - - - - - - - - - - -); -``` - -## 9. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_3_Testing_Polish/Task_3.4_Error_Handling_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Frontend_Dev) -- Task reference (Phase 3 / Task 3.4) -- Error handling strategy implemented -- Loading states and user feedback systems -- Validation and retry mechanisms -- Integration with existing components -- Testing approach for error scenarios - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_3.5_Accessibility.md b/prompts/tasks/Task_3.5_Accessibility.md deleted file mode 100644 index f0b481a..0000000 --- a/prompts/tasks/Task_3.5_Accessibility.md +++ /dev/null @@ -1,723 +0,0 @@ -# APM Task Assignment: Implement Accessibility and Final Polish - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 3, Task 3.5** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Ensure WCAG 2.1 AA compliance and polish the user experience with accessibility features, micro-interactions, and comprehensive documentation. - -**Prerequisites:** Tasks 3.1-3.4 completed - Testing infrastructure, performance optimization, and error handling should be in place. - -## 2. Detailed Action Steps - -1. **Implement WCAG 2.1 AA Compliance:** - - Add comprehensive ARIA labels and roles: - ```typescript - // src/components/tools/ToolCard.tsx - Enhanced accessibility - export function ToolCard({ tool, onViewDetails }: ToolCardProps) { - return ( - onViewDetails(tool)} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - onViewDetails(tool); - } - }} - sx={{ - cursor: 'pointer', - '&:hover': { transform: 'translateY(-2px)' }, - '&:focus': { - outline: '2px solid', - outlineColor: 'primary.main', - outlineOffset: '2px' - } - }} - > - - - {tool.name} - - - {tool.description} - - - {tool.tags.map((tag) => ( - - ))} - - - - ); - } - ``` - -2. **Implement Focus Management System:** - - Create focus management utilities: - ```typescript - // src/utils/focusManagement.ts - export class FocusManager { - private static focusHistory: HTMLElement[] = []; - - static saveFocus() { - const activeElement = document.activeElement as HTMLElement; - if (activeElement && activeElement !== document.body) { - this.focusHistory.push(activeElement); - } - } - - static restoreFocus() { - const lastFocused = this.focusHistory.pop(); - if (lastFocused) { - lastFocused.focus(); - } - } - - static trapFocus(container: HTMLElement) { - const focusableElements = container.querySelectorAll( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' - ); - const firstFocusable = focusableElements[0] as HTMLElement; - const lastFocusable = focusableElements[focusableElements.length - 1] as HTMLElement; - - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Tab') { - if (e.shiftKey) { - if (document.activeElement === firstFocusable) { - e.preventDefault(); - lastFocusable.focus(); - } - } else { - if (document.activeElement === lastFocusable) { - e.preventDefault(); - firstFocusable.focus(); - } - } - } - }; - - container.addEventListener('keydown', handleKeyDown); - return () => container.removeEventListener('keydown', handleKeyDown); - } - } - - // Custom hook for focus management - export function useFocusManagement() { - const restoreFocus = useRef(null); - - const saveFocus = useCallback(() => { - restoreFocus.current = document.activeElement as HTMLElement; - }, []); - - const restoreFocusTo = useCallback(() => { - if (restoreFocus.current) { - restoreFocus.current.focus(); - } - }, []); - - return { saveFocus, restoreFocus: restoreFocusTo }; - } - ``` - -3. **Implement Keyboard Navigation:** - - Enhanced search and filter keyboard support: - ```typescript - // src/components/tools/ToolSearch.tsx - Keyboard navigation - export function ToolSearch({ onSearch }: ToolSearchProps) { - const [value, setValue] = useState(''); - const [suggestions, setSuggestions] = useState([]); - const [selectedIndex, setSelectedIndex] = useState(-1); - - const handleKeyDown = (e: KeyboardEvent) => { - switch (e.key) { - case 'ArrowDown': - e.preventDefault(); - setSelectedIndex(prev => - prev < suggestions.length - 1 ? prev + 1 : prev - ); - break; - case 'ArrowUp': - e.preventDefault(); - setSelectedIndex(prev => prev > 0 ? prev - 1 : -1); - break; - case 'Enter': - e.preventDefault(); - if (selectedIndex >= 0) { - setValue(suggestions[selectedIndex]); - onSearch(suggestions[selectedIndex]); - } else { - onSearch(value); - } - setSuggestions([]); - break; - case 'Escape': - setSuggestions([]); - setSelectedIndex(-1); - break; - } - }; - - return ( - setValue(newValue)} - renderInput={(params) => ( - , - }} - /> - )} - /> - ); - } - ``` - -4. **Create Screen Reader Announcements:** - - Live region announcements: - ```typescript - // src/components/common/LiveRegion.tsx - export function LiveRegion() { - const [message, setMessage] = useState(''); - - useEffect(() => { - const announceUpdate = (event: CustomEvent) => { - setMessage(event.detail.message); - // Clear message after announcement - setTimeout(() => setMessage(''), 100); - }; - - window.addEventListener('announce', announceUpdate as EventListener); - return () => window.removeEventListener('announce', announceUpdate as EventListener); - }, []); - - return ( -
- {message} -
- ); - } - - // Utility function to announce messages - export function announceToScreenReader(message: string) { - window.dispatchEvent( - new CustomEvent('announce', { detail: { message } }) - ); - } - - // Usage in components - const handleToolsLoaded = (count: number) => { - announceToScreenReader(`${count} tools loaded and displayed`); - }; - ``` - -5. **Implement High Contrast and Dark Mode Support:** - - Enhanced theme with accessibility considerations: - ```typescript - // src/theme/accessibleTheme.ts - export const createAccessibleTheme = (mode: 'light' | 'dark' | 'high-contrast') => { - const baseTheme = createTheme({ - palette: { - mode: mode === 'high-contrast' ? 'dark' : mode, - ...(mode === 'high-contrast' && { - primary: { main: '#ffffff' }, - secondary: { main: '#ffff00' }, - background: { default: '#000000', paper: '#1a1a1a' }, - text: { primary: '#ffffff', secondary: '#ffff00' }, - error: { main: '#ff6b6b' }, - warning: { main: '#ffa726' }, - info: { main: '#29b6f6' }, - success: { main: '#66bb6a' }, - }), - }, - typography: { - // Ensure minimum 16px font size for accessibility - body1: { fontSize: '1rem', lineHeight: 1.6 }, - body2: { fontSize: '0.875rem', lineHeight: 1.6 }, - }, - components: { - MuiButton: { - styleOverrides: { - root: { - minHeight: 44, // Minimum touch target size - '&:focus': { - outline: '2px solid', - outlineOffset: '2px', - outlineColor: mode === 'high-contrast' ? '#ffff00' : '#1976d2', - }, - }, - }, - }, - MuiCard: { - styleOverrides: { - root: { - '&:focus': { - outline: '2px solid', - outlineOffset: '2px', - outlineColor: mode === 'high-contrast' ? '#ffff00' : '#1976d2', - }, - }, - }, - }, - }, - }); - - return baseTheme; - }; - ``` - -## 3. Performance Optimization for Accessibility - -**Reduce Motion for Users with Vestibular Disorders:** -```typescript -// src/hooks/useReducedMotion.ts -export function useReducedMotion() { - const [prefersReducedMotion, setPrefersReducedMotion] = useState(false); - - useEffect(() => { - const mediaQuery = window.matchMedia('(prefers-reduced-motion: reduce)'); - setPrefersReducedMotion(mediaQuery.matches); - - const handleChange = (e: MediaQueryListEvent) => { - setPrefersReducedMotion(e.matches); - }; - - mediaQuery.addEventListener('change', handleChange); - return () => mediaQuery.removeEventListener('change', handleChange); - }, []); - - return prefersReducedMotion; -} - -// Usage in components -export function AnimatedCard({ children }: { children: ReactNode }) { - const prefersReducedMotion = useReducedMotion(); - - return ( - - {children} - - ); -} -``` - -## 4. Comprehensive Testing for Accessibility - -**Automated Accessibility Testing:** -```typescript -// src/test-utils/accessibilityHelpers.ts -import { axe, toHaveNoViolations } from 'jest-axe'; - -expect.extend(toHaveNoViolations); - -export async function testAccessibility(container: HTMLElement) { - const results = await axe(container, { - rules: { - // Configure specific rules for your application - 'color-contrast': { enabled: true }, - 'keyboard-navigation': { enabled: true }, - 'focus-management': { enabled: true }, - 'aria-labels': { enabled: true }, - }, - }); - - expect(results).toHaveNoViolations(); - return results; -} - -// Integration test example -describe('ToolList Accessibility', () => { - it('should meet WCAG 2.1 AA standards', async () => { - const { container } = render(); - await testAccessibility(container); - }); - - it('should support keyboard navigation', async () => { - const { getByRole } = render(); - const firstTool = getByRole('button', { name: /test tool/i }); - - firstTool.focus(); - expect(firstTool).toHaveFocus(); - - fireEvent.keyDown(firstTool, { key: 'Enter' }); - // Assert navigation occurred - }); - - it('should announce content changes to screen readers', async () => { - const { rerender } = render(); - - rerender(); - - // Verify live region announcement - await waitFor(() => { - expect(screen.getByRole('status')).toHaveTextContent('3 tools loaded and displayed'); - }); - }); -}); -``` - -## 5. Micro-Interactions and Polish - -**Enhanced User Experience:** -```typescript -// src/components/common/InteractiveButton.tsx -export function InteractiveButton({ - children, - onClick, - loading = false, - ...props -}: InteractiveButtonProps) { - const [isPressed, setIsPressed] = useState(false); - const prefersReducedMotion = useReducedMotion(); - - const handleMouseDown = () => setIsPressed(true); - const handleMouseUp = () => setIsPressed(false); - const handleMouseLeave = () => setIsPressed(false); - - return ( - - ); -} - -// Tooltip enhancements -export function AccessibleTooltip({ - title, - children, - ...props -}: TooltipProps) { - const [open, setOpen] = useState(false); - - return ( - setOpen(true)} - onClose={() => setOpen(false)} - enterDelay={500} - leaveDelay={200} - arrow - PopperProps={{ - 'aria-describedby': props.id, - role: 'tooltip', - }} - > - setOpen(true)} - onMouseLeave={() => setOpen(false)} - onFocus={() => setOpen(true)} - onBlur={() => setOpen(false)} - > - {children} - - - ); -} -``` - -## 6. Documentation and Help System - -**Comprehensive Documentation Components:** -```typescript -// src/components/help/HelpSystem.tsx -export function HelpSystem() { - const [isOpen, setIsOpen] = useState(false); - const [activeSection, setActiveSection] = useState('overview'); - - return ( - <> - setIsOpen(true)} - > - - - - setIsOpen(false)} - maxWidth="md" - fullWidth - aria-labelledby="help-title" - > - - ToolVault Help & Documentation - - - - - {helpSections.map((section) => ( - setActiveSection(section.id)} - > - {section.icon} - - - ))} - - - - - - - - - - - - ); -} - -// Contextual help tooltips -export function ContextualHelp({ content, placement = 'top' }: ContextualHelpProps) { - return ( - - - - - - ); -} -``` - -## 7. Keyboard Shortcuts - -**Comprehensive Keyboard Navigation:** -```typescript -// src/hooks/useKeyboardShortcuts.ts -export function useKeyboardShortcuts() { - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - // Global shortcuts - if (e.ctrlKey || e.metaKey) { - switch (e.key) { - case 'k': - e.preventDefault(); - // Focus search - const searchInput = document.querySelector('[aria-label*="Search"]') as HTMLElement; - searchInput?.focus(); - break; - case '/': - e.preventDefault(); - // Open help - const helpButton = document.querySelector('[aria-label="Open help"]') as HTMLElement; - helpButton?.click(); - break; - case 'h': - e.preventDefault(); - // Navigate home - window.location.href = '/'; - break; - } - } - - // Escape key handling - if (e.key === 'Escape') { - // Close any open dialogs or clear focus - const dialogs = document.querySelectorAll('[role="dialog"]'); - if (dialogs.length > 0) { - const closeButton = dialogs[dialogs.length - 1].querySelector('button[aria-label*="close"]') as HTMLElement; - closeButton?.click(); - } - } - }; - - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, []); -} - -// Keyboard shortcut help component -export function KeyboardShortcuts() { - const shortcuts = [ - { key: 'Ctrl/Cmd + K', description: 'Focus search' }, - { key: 'Ctrl/Cmd + /', description: 'Open help' }, - { key: 'Ctrl/Cmd + H', description: 'Go to homepage' }, - { key: 'Tab', description: 'Navigate between elements' }, - { key: 'Enter/Space', description: 'Activate focused element' }, - { key: 'Escape', description: 'Close dialogs or clear focus' }, - ]; - - return ( - - - - Keyboard Shortcut - Action - - - - {shortcuts.map((shortcut) => ( - - - - - {shortcut.description} - - ))} - -
- ); -} -``` - -## 8. Expected Output & Deliverables - -**Success Criteria:** -- WCAG 2.1 AA compliance verified through automated and manual testing -- Complete keyboard navigation support -- Screen reader compatibility confirmed -- High contrast and reduced motion support -- Comprehensive documentation and help system -- All interactive elements have proper focus indicators -- Live regions announce important state changes - -**Deliverables:** -1. **Accessibility Infrastructure:** - - `src/components/common/LiveRegion.tsx` - - `src/utils/focusManagement.ts` - - `src/hooks/useFocusManagement.ts` - - `src/hooks/useReducedMotion.ts` - - `src/hooks/useKeyboardShortcuts.ts` - -2. **Enhanced Components:** - - Updated `src/components/tools/ToolCard.tsx` with ARIA support - - Updated `src/components/tools/ToolSearch.tsx` with keyboard nav - - `src/components/common/InteractiveButton.tsx` - - `src/components/common/AccessibleTooltip.tsx` - -3. **Theme and Visual:** - - `src/theme/accessibleTheme.ts` - - High contrast theme implementation - - Focus indicator styling - -4. **Documentation System:** - - `src/components/help/HelpSystem.tsx` - - `src/components/help/ContextualHelp.tsx` - - `src/components/help/KeyboardShortcuts.tsx` - - Component documentation files - -5. **Testing Infrastructure:** - - `src/test-utils/accessibilityHelpers.ts` - - Accessibility test suites for all components - - Manual testing checklist - -## 9. Manual Testing Checklist - -**Accessibility Testing Checklist:** -- [ ] All interactive elements are keyboard accessible -- [ ] Focus indicators are visible and consistent -- [ ] Screen reader announcements are meaningful and timely -- [ ] Color contrast ratios meet WCAG AA standards -- [ ] All images have appropriate alt text -- [ ] Forms have proper labels and error messages -- [ ] Keyboard shortcuts work as expected -- [ ] High contrast mode is fully functional -- [ ] Reduced motion preferences are respected -- [ ] Page titles and headings create logical structure - -## 10. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_3_Testing_Polish/Task_3.5_Accessibility_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_QA_Specialist) -- Task reference (Phase 3 / Task 3.5) -- WCAG compliance level achieved -- Accessibility features implemented -- Keyboard navigation capabilities -- Screen reader compatibility details -- Testing approach and results -- Documentation and help system features - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_4.1_Mock_Backend.md b/prompts/tasks/Task_4.1_Mock_Backend.md deleted file mode 100644 index 6321c61..0000000 --- a/prompts/tasks/Task_4.1_Mock_Backend.md +++ /dev/null @@ -1,476 +0,0 @@ -# APM Task Assignment: Implement Mock Backend Service - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 4, Task 4.1** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Create REST mock server providing canned responses for tool execution to enable full UI functionality without real backend infrastructure. - -**Prerequisites:** Phase 3 completed - Testing infrastructure, performance optimization, error handling, and accessibility should be fully implemented. - -## 2. Detailed Action Steps - -1. **Set up Mock Service Infrastructure:** - - Configure MSW (Mock Service Worker) for comprehensive API mocking: - ```typescript - // src/mocks/browser.ts - import { setupWorker } from 'msw/browser'; - import { toolExecutionHandlers } from './handlers/toolExecutionHandlers'; - import { metadataHandlers } from './handlers/metadataHandlers'; - - export const worker = setupWorker( - ...toolExecutionHandlers, - ...metadataHandlers - ); - - // Enable in development - if (process.env.NODE_ENV === 'development') { - worker.start({ - onUnhandledRequest: 'warn', - quiet: false, - }); - } - ``` - - Create mock service directory structure: - ``` - src/mocks/ - ├── browser.ts - ├── server.ts (for Node.js/testing) - ├── handlers/ - │ ├── toolExecutionHandlers.ts - │ ├── metadataHandlers.ts - │ └── errorHandlers.ts - ├── data/ - │ ├── executionResults/ - │ ├── mockData.ts - │ └── scenarios.ts - └── utils/ - ├── responseHelpers.ts - └── executionSimulator.ts - ``` - -2. **Create Mock Tool Execution Responses:** - - Generate realistic execution results by tool type: - ```typescript - // src/mocks/data/executionResults.ts - export const mockExecutionResults = { - 'wordcount': { - success: { - executionId: 'exec_wordcount_123', - status: 'completed', - duration: 1250, - results: { - wordCount: 1247, - characterCount: 7832, - paragraphCount: 12, - averageWordsPerSentence: 15.2, - readingTime: '5 minutes', - wordFrequency: [ - { word: 'analysis', count: 23 }, - { word: 'data', count: 18 }, - { word: 'tool', count: 15 }, - ], - }, - }, - error: { - executionId: 'exec_wordcount_err', - status: 'failed', - error: { - code: 'INVALID_INPUT', - message: 'Input text is empty or contains invalid characters', - details: 'The provided text input must contain at least 1 character', - }, - }, - }, - 'geospatial-analysis': { - success: { - executionId: 'exec_geo_456', - status: 'completed', - duration: 3450, - results: { - type: 'FeatureCollection', - features: [ - { - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [-122.4194, 37.7749], - }, - properties: { - name: 'San Francisco', - population: 883305, - area: 121.4, - }, - }, - ], - metadata: { - totalFeatures: 1, - boundingBox: [-122.5194, 37.6749, -122.3194, 37.8749], - projection: 'EPSG:4326', - }, - }, - }, - }, - }; - ``` - - Include various data formats (GeoJSON, CSV data, charts data, binary files) - - Mock execution timing with realistic delays - -3. **Implement Execution State Management:** - - Create execution status tracking system: - ```typescript - // src/mocks/utils/executionSimulator.ts - export class ExecutionSimulator { - private executions: Map = new Map(); - - async startExecution( - toolId: string, - inputs: Record - ): Promise { - const executionId = `exec_${toolId}_${Date.now()}`; - const execution: ExecutionState = { - id: executionId, - toolId, - inputs, - status: 'running', - startTime: new Date(), - progress: 0, - }; - - this.executions.set(executionId, execution); - - // Simulate execution with progress updates - this.simulateProgress(executionId); - - return { - executionId, - status: 'running', - message: 'Tool execution started successfully', - }; - } - - private async simulateProgress(executionId: string) { - const execution = this.executions.get(executionId)!; - const duration = this.getExecutionDuration(execution.toolId); - - const progressInterval = setInterval(() => { - execution.progress += Math.random() * 20; - - if (execution.progress >= 100) { - clearInterval(progressInterval); - this.completeExecution(executionId); - } - }, duration / 10); - } - - private completeExecution(executionId: string) { - const execution = this.executions.get(executionId)!; - execution.status = Math.random() > 0.1 ? 'completed' : 'failed'; - execution.endTime = new Date(); - - if (execution.status === 'completed') { - execution.results = this.generateResults(execution.toolId, execution.inputs); - } else { - execution.error = this.generateError(execution.toolId); - } - } - } - ``` - - Add execution progress indicators with realistic progress curves - - Implement cancellation capability with cleanup - -4. **Wire Mock Service to Frontend:** - - Update toolVaultService to use mock endpoints: - ```typescript - // src/services/toolExecutionService.ts - export interface ToolExecutionService { - executeToool(toolId: string, inputs: Record): Promise; - getExecutionStatus(executionId: string): Promise; - cancelExecution(executionId: string): Promise; - getExecutionResults(executionId: string): Promise; - } - - class MockToolExecutionService implements ToolExecutionService { - async executeTool(toolId: string, inputs: Record): Promise { - const response = await fetch('/api/tools/execute', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ toolId, inputs }), - }); - - if (!response.ok) { - throw new ToolExecutionError(`Execution failed: ${response.statusText}`, { - type: 'execution_failed', - code: response.status.toString(), - retryable: response.status >= 500, - }); - } - - return response.json(); - } - - async getExecutionStatus(executionId: string): Promise { - const response = await fetch(`/api/executions/${executionId}/status`); - return response.json(); - } - } - - export const toolExecutionService = new MockToolExecutionService(); - ``` - - Handle authentication and error responses appropriately - - Configure development vs production service switching - -## 3. Mock API Endpoints Design - -**RESTful API Structure:** -```typescript -// src/mocks/handlers/toolExecutionHandlers.ts -export const toolExecutionHandlers = [ - // Start tool execution - rest.post('/api/tools/execute', async (req, res, ctx) => { - const { toolId, inputs } = await req.json(); - - // Validate inputs - const validation = validateToolInputs(toolId, inputs); - if (!validation.isValid) { - return res( - ctx.status(400), - ctx.json({ - error: 'INVALID_INPUT', - message: 'Input validation failed', - details: validation.errors, - }) - ); - } - - const execution = await executionSimulator.startExecution(toolId, inputs); - return res(ctx.status(202), ctx.json(execution)); - }), - - // Get execution status and progress - rest.get('/api/executions/:executionId/status', (req, res, ctx) => { - const { executionId } = req.params; - const status = executionSimulator.getStatus(executionId); - - if (!status) { - return res( - ctx.status(404), - ctx.json({ error: 'Execution not found' }) - ); - } - - return res(ctx.json(status)); - }), - - // Get execution results - rest.get('/api/executions/:executionId/results', (req, res, ctx) => { - const { executionId } = req.params; - const results = executionSimulator.getResults(executionId); - - if (!results) { - return res( - ctx.status(404), - ctx.json({ error: 'Results not found' }) - ); - } - - return res(ctx.json(results)); - }), - - // Cancel execution - rest.delete('/api/executions/:executionId', (req, res, ctx) => { - const { executionId } = req.params; - executionSimulator.cancelExecution(executionId); - - return res(ctx.status(204)); - }), -]; -``` - -## 4. Advanced Mock Scenarios - -**Error Simulation:** -```typescript -// src/mocks/data/scenarios.ts -export const mockScenarios = { - networkErrors: { - probability: 0.05, // 5% chance of network errors - types: ['timeout', 'connection_refused', 'dns_failure'], - }, - executionErrors: { - probability: 0.1, // 10% chance of execution errors - types: [ - { code: 'INVALID_INPUT', probability: 0.4 }, - { code: 'RESOURCE_EXHAUSTED', probability: 0.3 }, - { code: 'TOOL_UNAVAILABLE', probability: 0.2 }, - { code: 'INTERNAL_ERROR', probability: 0.1 }, - ], - }, - slowExecution: { - probability: 0.15, // 15% chance of slow execution - multiplier: 3, // 3x longer than normal - }, -}; - -// Dynamic scenario application -export function applyScenario(toolId: string, inputs: any) { - if (Math.random() < mockScenarios.networkErrors.probability) { - throw new NetworkError(getRandomNetworkError()); - } - - if (Math.random() < mockScenarios.executionErrors.probability) { - throw new ExecutionError(getRandomExecutionError()); - } - - const baseDuration = getToolBaseDuration(toolId); - const duration = Math.random() < mockScenarios.slowExecution.probability - ? baseDuration * mockScenarios.slowExecution.multiplier - : baseDuration; - - return { duration, shouldFail: false }; -} -``` - -## 5. Testing Integration - -**MSW Testing Setup:** -```typescript -// src/test-utils/mswSetup.ts -import { setupServer } from 'msw/node'; -import { toolExecutionHandlers } from '../mocks/handlers/toolExecutionHandlers'; - -export const server = setupServer(...toolExecutionHandlers); - -// Test setup -beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); -afterEach(() => server.resetHandlers()); -afterAll(() => server.close()); - -// Test utilities -export const mockSuccessfulExecution = (toolId: string, results: any) => { - server.use( - rest.post('/api/tools/execute', (req, res, ctx) => { - return res(ctx.json({ - executionId: `test_${toolId}`, - status: 'completed', - results - })); - }) - ); -}; -``` - -## 6. Performance Considerations - -**Optimized Mock Responses:** -```typescript -// src/mocks/utils/responseHelpers.ts -export class ResponseCache { - private cache = new Map(); - private readonly TTL = 5 * 60 * 1000; // 5 minutes - - get(key: string): any | null { - const entry = this.cache.get(key); - if (!entry) return null; - - if (Date.now() - entry.timestamp > this.TTL) { - this.cache.delete(key); - return null; - } - - return entry.data; - } - - set(key: string, data: any): void { - this.cache.set(key, { data, timestamp: Date.now() }); - } -} - -// Streaming mock responses for large datasets -export function createStreamingResponse(data: any[]) { - const encoder = new TextEncoder(); - const stream = new ReadableStream({ - start(controller) { - data.forEach((chunk, index) => { - const json = JSON.stringify(chunk) + (index < data.length - 1 ? ',' : ''); - controller.enqueue(encoder.encode(json)); - }); - controller.close(); - } - }); - - return new Response(stream, { - headers: { 'Content-Type': 'application/json' } - }); -} -``` - -## 7. Expected Output & Deliverables - -**Success Criteria:** -- Complete mock API covering all tool execution workflows -- Realistic execution timing and progress updates -- Comprehensive error scenario simulation -- Development/production environment switching -- Integration with existing service layer -- Performance optimized mock responses - -**Deliverables:** -1. **Mock Service Infrastructure:** - - `src/mocks/browser.ts` - MSW browser setup - - `src/mocks/server.ts` - MSW Node.js setup - - `src/mocks/handlers/toolExecutionHandlers.ts` - API handlers - - `src/mocks/utils/executionSimulator.ts` - Execution simulation - -2. **Mock Data and Scenarios:** - - `src/mocks/data/executionResults.ts` - Sample results - - `src/mocks/data/scenarios.ts` - Error scenarios - - `src/mocks/utils/responseHelpers.ts` - Utilities - -3. **Service Integration:** - - `src/services/toolExecutionService.ts` - Execution service - - Enhanced `src/services/toolVaultService.ts` - Integration - - `src/types/execution.ts` - Type definitions - -4. **Testing Support:** - - `src/test-utils/mswSetup.ts` - Test configuration - - Mock scenario utilities for testing - - Performance testing helpers - -## 8. Development vs Production Configuration - -**Environment Switching:** -```typescript -// src/services/serviceFactory.ts -export function createToolExecutionService(): ToolExecutionService { - if (process.env.NODE_ENV === 'development' || process.env.VITE_USE_MOCK_API === 'true') { - return new MockToolExecutionService(); - } else { - return new ProductionToolExecutionService(); - } -} - -// src/main.tsx integration -if (process.env.NODE_ENV === 'development') { - import('./mocks/browser').then(({ worker }) => { - worker.start(); - }); -} -``` - -## 9. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_4_Full_UI_Mock_Execution/Task_4.1_Mock_Backend_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Frontend_Dev) -- Task reference (Phase 4 / Task 4.1) -- Mock service architecture and API design -- Execution simulation strategies -- Error scenario implementation -- Performance optimization approaches -- Testing integration details -- Development/production configuration - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_4.2_Enhanced_Search.md b/prompts/tasks/Task_4.2_Enhanced_Search.md deleted file mode 100644 index a47bca0..0000000 --- a/prompts/tasks/Task_4.2_Enhanced_Search.md +++ /dev/null @@ -1,677 +0,0 @@ -# APM Task Assignment: Enhanced Search and Filtering - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 4, Task 4.2** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Implement advanced search and filter capabilities with badges, metadata-driven features, and comprehensive filtering system to enhance tool discoverability. - -**Prerequisites:** Task 4.1 completed - Mock backend service should be operational and providing realistic data. - -## 2. Detailed Action Steps - -1. **Implement Advanced Search Features:** - - Create full-text search with highlighting: - ```typescript - // src/utils/searchUtils.ts - export interface SearchOptions { - query: string; - fields: SearchField[]; - fuzzy: boolean; - highlightMatches: boolean; - maxResults: number; - } - - export interface SearchResult { - item: T; - score: number; - matches: SearchMatch[]; - } - - export class AdvancedSearchEngine { - private index: FlexSearch.Index; - private documents: Map = new Map(); - - constructor() { - this.index = new FlexSearch.Index({ - preset: 'performance', - tokenize: 'full', - resolution: 3, - minlength: 2, - }); - } - - indexTool(tool: Tool): void { - const searchableContent = this.createSearchableContent(tool); - this.index.add(tool.id, searchableContent); - this.documents.set(tool.id, tool); - } - - search(options: SearchOptions): SearchResult[] { - const results = this.index.search(options.query, options.maxResults); - return results.map(id => ({ - item: this.documents.get(id as string)!, - score: this.calculateRelevanceScore(id as string, options.query), - matches: this.findMatches(id as string, options.query), - })); - } - - private createSearchableContent(tool: Tool): string { - return [ - tool.name, - tool.description, - tool.category, - ...tool.tags, - ...tool.inputs.map(input => input.label), - ...tool.outputs.map(output => output.label), - ].join(' ').toLowerCase(); - } - } - ``` - -2. **Build Comprehensive Filtering System:** - - Create advanced filter panel: - ```typescript - // src/components/tools/AdvancedFilters.tsx - export interface FilterOptions { - categories: string[]; - tags: string[]; - complexity: 'beginner' | 'intermediate' | 'advanced' | null; - lastUpdated: 'week' | 'month' | 'year' | null; - inputTypes: string[]; - outputTypes: string[]; - status: 'new' | 'updated' | 'stable' | 'deprecated' | null; - } - - export function AdvancedFilters({ - filters, - onFiltersChange, - availableOptions - }: AdvancedFiltersProps) { - const [isExpanded, setIsExpanded] = useState(false); - - return ( - - setIsExpanded(!isExpanded)} - aria-label={isExpanded ? 'Collapse filters' : 'Expand filters'} - > - {isExpanded ? : } - - } - /> - - - {/* Category Filter */} - - - {availableOptions.categories.map(category => ( - handleCategoryChange(category.id, e.target.checked)} - /> - } - label={ - - {category.name} - - - } - /> - ))} - - - - {/* Tag Filter with Multi-select */} - - onFiltersChange({ ...filters, tags: newTags })} - renderTags={(value, getTagProps) => - value.map((option, index) => ( - - )) - } - renderInput={(params) => ( - - )} - /> - - - {/* Complexity Filter */} - - onFiltersChange({ - ...filters, - complexity: e.target.value || null - })} - > - } label="Any" /> - } label="Beginner" /> - } label="Intermediate" /> - } label="Advanced" /> - - - - {/* Date-based Filter */} - - - - - - - ); - } - ``` - -3. **Create Metadata-Driven Badges:** - - Implement dynamic badge system: - ```typescript - // src/components/tools/ToolBadges.tsx - export interface BadgeConfig { - type: 'new' | 'updated' | 'popular' | 'beta' | 'stable' | 'deprecated'; - label: string; - color: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'info'; - icon?: React.ReactNode; - condition: (tool: Tool, metadata?: ToolMetadata) => boolean; - } - - const badgeConfigs: BadgeConfig[] = [ - { - type: 'new', - label: 'New', - color: 'primary', - icon: , - condition: (tool, metadata) => { - const createdDate = new Date(metadata?.created || tool.created); - const daysSinceCreation = (Date.now() - createdDate.getTime()) / (1000 * 60 * 60 * 24); - return daysSinceCreation <= 7; - } - }, - { - type: 'updated', - label: 'Updated', - color: 'info', - icon: , - condition: (tool, metadata) => { - const updatedDate = new Date(metadata?.updated || tool.updated); - const daysSinceUpdate = (Date.now() - updatedDate.getTime()) / (1000 * 60 * 60 * 24); - return daysSinceUpdate <= 30; - } - }, - { - type: 'popular', - label: 'Popular', - color: 'success', - icon: , - condition: (tool, metadata) => metadata?.usage?.executions > 1000 - }, - { - type: 'beta', - label: 'Beta', - color: 'warning', - condition: (tool, metadata) => metadata?.status === 'beta' - } - ]; - - export function ToolBadges({ tool, metadata }: ToolBadgesProps) { - const applicableBadges = badgeConfigs.filter(config => - config.condition(tool, metadata) - ); - - if (applicableBadges.length === 0) return null; - - return ( - - {applicableBadges.map(badge => ( - - ))} - - ); - } - ``` - -4. **Enhance UI with Filter Visualizations:** - - Create filter summary and management: - ```typescript - // src/components/tools/FilterSummary.tsx - export function FilterSummary({ - filters, - onClearFilter, - onClearAll, - resultCount - }: FilterSummaryProps) { - const activeFilters = getActiveFilters(filters); - - if (activeFilters.length === 0) { - return ( - - Showing all {resultCount} tools - - ); - } - - return ( - - - - Showing {resultCount} tools with filters: - - - - - {activeFilters.map(filter => ( - onClearFilter(filter.key)} - size="small" - variant="outlined" - /> - ))} - - - ); - } - - // src/components/tools/FilterPresets.tsx - export function FilterPresets({ onApplyPreset }: FilterPresetsProps) { - const presets: FilterPreset[] = [ - { - name: 'GIS Tools', - icon: , - filters: { - categories: ['geospatial'], - tags: ['gis', 'mapping', 'spatial'], - } - }, - { - name: 'Text Analysis', - icon: , - filters: { - categories: ['text'], - tags: ['nlp', 'analysis', 'processing'], - } - }, - { - name: 'Data Visualization', - icon: , - filters: { - categories: ['visualization'], - outputTypes: ['chart', 'graph'], - } - }, - { - name: 'Recently Updated', - icon: , - filters: { - lastUpdated: 'month', - } - } - ]; - - return ( - - - - - {presets.map(preset => ( - - - - ))} - - - - ); - } - ``` - -## 3. Advanced Search Implementation - -**Search with Autocomplete and Suggestions:** -```typescript -// src/components/tools/SearchWithSuggestions.tsx -export function SearchWithSuggestions({ onSearch, tools }: SearchProps) { - const [query, setQuery] = useState(''); - const [suggestions, setSuggestions] = useState([]); - const [searchHistory, setSearchHistory] = useState([]); - - const debouncedQuery = useDebouncedValue(query, 300); - - useEffect(() => { - if (debouncedQuery.length >= 2) { - generateSuggestions(debouncedQuery); - } else { - setSuggestions([]); - } - }, [debouncedQuery, tools]); - - const generateSuggestions = useCallback((searchQuery: string) => { - const suggestions: SearchSuggestion[] = []; - - // Tool name suggestions - const nameMatches = tools - .filter(tool => tool.name.toLowerCase().includes(searchQuery.toLowerCase())) - .slice(0, 3) - .map(tool => ({ - type: 'tool' as const, - value: tool.name, - label: tool.name, - icon: , - description: tool.description, - })); - - // Category suggestions - const categories = [...new Set(tools.map(tool => tool.category))] - .filter(category => category.toLowerCase().includes(searchQuery.toLowerCase())) - .slice(0, 2) - .map(category => ({ - type: 'category' as const, - value: category, - label: `Category: ${category}`, - icon: , - })); - - // Tag suggestions - const allTags = [...new Set(tools.flatMap(tool => tool.tags))] - .filter(tag => tag.toLowerCase().includes(searchQuery.toLowerCase())) - .slice(0, 3) - .map(tag => ({ - type: 'tag' as const, - value: tag, - label: `Tag: ${tag}`, - icon: , - })); - - suggestions.push(...nameMatches, ...categories, ...allTags); - setSuggestions(suggestions); - }, [tools]); - - const handleSearch = (searchQuery: string) => { - if (searchQuery.trim()) { - // Add to search history - setSearchHistory(prev => [ - searchQuery, - ...prev.filter(item => item !== searchQuery) - ].slice(0, 10)); - - onSearch(searchQuery); - } - }; - - return ( - - setQuery(newValue)} - onChange={(_, value) => { - if (typeof value === 'string') { - handleSearch(value); - } else if (value) { - handleSearch(value.value); - } - }} - getOptionLabel={(option) => - typeof option === 'string' ? option : option.label - } - renderOption={(props, option) => ( -
  • - - {option.icon} - - -
  • - )} - renderInput={(params) => ( - , - endAdornment: query && ( - setQuery('')} - edge="end" - aria-label="Clear search" - > - - - ), - }} - /> - )} - /> - - {/* Search History */} - {query === '' && searchHistory.length > 0 && ( - - - - Recent searches - - - {searchHistory.slice(0, 5).map((item, index) => ( - handleSearch(item)} - > - - - - - - ))} - - - - )} -
    - ); -} -``` - -## 4. Performance Optimization for Search - -**Optimized Search Performance:** -```typescript -// src/hooks/useOptimizedSearch.ts -export function useOptimizedSearch(tools: Tool[]) { - const searchEngine = useRef(); - const [isIndexing, setIsIndexing] = useState(false); - - // Initialize and build search index - useEffect(() => { - if (!searchEngine.current) { - searchEngine.current = new AdvancedSearchEngine(); - } - - const buildIndex = async () => { - setIsIndexing(true); - - // Use web worker for heavy indexing - const worker = new Worker(new URL('../workers/searchIndexWorker.ts', import.meta.url)); - - worker.postMessage({ tools }); - - worker.onmessage = (e) => { - if (e.data.type === 'INDEX_COMPLETE') { - searchEngine.current!.loadIndex(e.data.index); - setIsIndexing(false); - } - }; - }; - - if (tools.length > 100) { - buildIndex(); - } else { - // Build index synchronously for small datasets - tools.forEach(tool => searchEngine.current!.indexTool(tool)); - } - }, [tools]); - - const search = useCallback((options: SearchOptions): SearchResult[] => { - if (!searchEngine.current || isIndexing) { - return []; - } - - return searchEngine.current.search(options); - }, [isIndexing]); - - return { search, isIndexing }; -} - -// src/workers/searchIndexWorker.ts -import { AdvancedSearchEngine } from '../utils/searchUtils'; - -self.onmessage = (e) => { - const { tools } = e.data; - const engine = new AdvancedSearchEngine(); - - tools.forEach((tool: Tool) => { - engine.indexTool(tool); - }); - - self.postMessage({ - type: 'INDEX_COMPLETE', - index: engine.exportIndex(), - }); -}; -``` - -## 5. URL State Management - -**Deep Linking for Search and Filters:** -```typescript -// src/hooks/useSearchState.ts -export function useSearchState() { - const [searchParams, setSearchParams] = useSearchParams(); - - const searchState = useMemo(() => ({ - query: searchParams.get('q') || '', - categories: searchParams.getAll('category'), - tags: searchParams.getAll('tag'), - complexity: searchParams.get('complexity') as FilterOptions['complexity'], - lastUpdated: searchParams.get('updated') as FilterOptions['lastUpdated'], - }), [searchParams]); - - const updateSearchState = useCallback((updates: Partial) => { - const newParams = new URLSearchParams(); - - if (updates.query) newParams.set('q', updates.query); - updates.categories?.forEach(cat => newParams.append('category', cat)); - updates.tags?.forEach(tag => newParams.append('tag', tag)); - if (updates.complexity) newParams.set('complexity', updates.complexity); - if (updates.lastUpdated) newParams.set('updated', updates.lastUpdated); - - setSearchParams(newParams, { replace: true }); - }, [setSearchParams]); - - return [searchState, updateSearchState] as const; -} -``` - -## 6. Expected Output & Deliverables - -**Success Criteria:** -- Full-text search with highlighting and relevance scoring -- Comprehensive filter system with multiple criteria -- Dynamic badge system based on tool metadata -- Filter presets for common use cases -- Search autocomplete with suggestions and history -- URL state management for shareable filtered views -- Performance optimization for large datasets - -**Deliverables:** -1. **Search Infrastructure:** - - `src/utils/searchUtils.ts` - Advanced search engine - - `src/workers/searchIndexWorker.ts` - Web worker for indexing - - `src/hooks/useOptimizedSearch.ts` - Optimized search hook - - `src/hooks/useSearchState.ts` - URL state management - -2. **Filter Components:** - - `src/components/tools/AdvancedFilters.tsx` - Main filter panel - - `src/components/tools/FilterSummary.tsx` - Active filters display - - `src/components/tools/FilterPresets.tsx` - Quick filter presets - - `src/components/tools/SearchWithSuggestions.tsx` - Enhanced search - -3. **Badge System:** - - `src/components/tools/ToolBadges.tsx` - Dynamic badge display - - `src/utils/badgeUtils.ts` - Badge configuration and logic - - Enhanced `src/components/tools/ToolCard.tsx` - Badge integration - -4. **Enhanced UI:** - - Updated `src/components/tools/ToolList.tsx` - Search integration - - Filter visualization and management - - Search result highlighting - -## 7. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_4_Full_UI_Mock_Execution/Task_4.2_Enhanced_Search_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_UI_Specialist) -- Task reference (Phase 4 / Task 4.2) -- Search engine implementation details -- Filter system architecture -- Badge system configuration -- Performance optimization strategies -- URL state management approach -- User experience enhancements - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_4.3_Output_Rendering.md b/prompts/tasks/Task_4.3_Output_Rendering.md deleted file mode 100644 index a811246..0000000 --- a/prompts/tasks/Task_4.3_Output_Rendering.md +++ /dev/null @@ -1,845 +0,0 @@ -# APM Task Assignment: Advanced Output Rendering - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 4, Task 4.3** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Implement production-grade output viewers for different data types including LeafletJS map rendering, interactive tables, charts, and universal output rendering capabilities. - -**Prerequisites:** Tasks 4.1 and 4.2 completed - Mock backend service and enhanced search/filtering should be operational. - -## 2. Detailed Action Steps - -1. **Implement LeafletJS Map Rendering:** - - Set up React-Leaflet for GeoJSON visualization: - ```typescript - // src/components/output/MapViewer.tsx - import { MapContainer, TileLayer, GeoJSON, Marker, Popup } from 'react-leaflet'; - import L from 'leaflet'; - import 'leaflet/dist/leaflet.css'; - - export interface MapViewerProps { - data: GeoJSON.FeatureCollection; - height?: number; - width?: string; - interactive?: boolean; - showControls?: boolean; - } - - export function MapViewer({ - data, - height = 400, - width = '100%', - interactive = true, - showControls = true - }: MapViewerProps) { - const [bounds, setBounds] = useState(null); - const [selectedFeature, setSelectedFeature] = useState(null); - - useEffect(() => { - if (data.features.length > 0) { - const group = L.geoJSON(data); - setBounds(group.getBounds()); - } - }, [data]); - - const onEachFeature = (feature: GeoJSON.Feature, layer: L.Layer) => { - if (feature.properties) { - layer.bindPopup( - setSelectedFeature(feature)} - /> - ); - } - - layer.on({ - mouseover: (e) => { - const layer = e.target; - layer.setStyle({ weight: 3, color: '#666', fillOpacity: 0.7 }); - }, - mouseout: (e) => { - const layer = e.target; - layer.setStyle({ weight: 2, color: '#3388ff', fillOpacity: 0.2 }); - }, - }); - }; - - return ( - - - - - {data && ( - ({ - color: '#3388ff', - weight: 2, - fillOpacity: 0.2, - })} - /> - )} - - - {/* Map Controls */} - {showControls && ( - - - - - - - - )} - - {/* Feature Details Panel */} - {selectedFeature && ( - - setSelectedFeature(null)} - /> - - )} - - ); - } - ``` - -2. **Create Interactive Table Viewer:** - - Build comprehensive data table with advanced features: - ```typescript - // src/components/output/TableViewer.tsx - import { DataGrid, GridColDef, GridRowParams } from '@mui/x-data-grid'; - - export interface TableViewerProps { - data: any[]; - columns?: GridColDef[]; - height?: number; - searchable?: boolean; - exportable?: boolean; - selectable?: boolean; - } - - export function TableViewer({ - data, - columns, - height = 400, - searchable = true, - exportable = true, - selectable = false, - }: TableViewerProps) { - const [searchText, setSearchText] = useState(''); - const [selectedRows, setSelectedRows] = useState([]); - - // Auto-generate columns if not provided - const tableColumns = useMemo(() => { - if (columns) return columns; - - if (data.length === 0) return []; - - const sampleRow = data[0]; - return Object.keys(sampleRow).map((key): GridColDef => ({ - field: key, - headerName: key.charAt(0).toUpperCase() + key.slice(1), - width: getColumnWidth(key, sampleRow[key]), - type: inferColumnType(sampleRow[key]), - filterable: true, - sortable: true, - renderCell: (params) => renderCellContent(params.value), - })); - }, [data, columns]); - - const filteredData = useMemo(() => { - if (!searchText) return data; - - return data.filter(row => - Object.values(row).some(value => - String(value).toLowerCase().includes(searchText.toLowerCase()) - ) - ); - }, [data, searchText]); - - const handleExport = (format: 'csv' | 'json' | 'excel') => { - const exportData = selectedRows.length > 0 - ? filteredData.filter((_, index) => selectedRows.includes(index)) - : filteredData; - - switch (format) { - case 'csv': - exportToCSV(exportData, tableColumns); - break; - case 'json': - exportToJSON(exportData); - break; - case 'excel': - exportToExcel(exportData, tableColumns); - break; - } - }; - - return ( - - {/* Table Controls */} - - {searchable && ( - setSearchText(e.target.value)} - InputProps={{ - startAdornment: , - }} - sx={{ minWidth: 250 }} - /> - )} - - {exportable && ( - - - - - - )} - - - {/* Data Grid */} - ({ id: index, ...row }))} - columns={tableColumns} - checkboxSelection={selectable} - disableRowSelectionOnClick={!selectable} - onRowSelectionModelChange={setSelectedRows} - density="compact" - sx={{ - '& .MuiDataGrid-cell': { - borderBottom: '1px solid #e0e0e0', - }, - '& .MuiDataGrid-row:hover': { - backgroundColor: 'action.hover', - }, - }} - /> - - {/* Table Summary */} - - - Showing {filteredData.length} of {data.length} rows - {selectedRows.length > 0 && ` (${selectedRows.length} selected)`} - - - {selectedRows.length > 0 && ( - - )} - - - ); - } - ``` - -3. **Build Chart Visualization Components:** - - Implement Chart.js integration for multiple chart types: - ```typescript - // src/components/output/ChartViewer.tsx - import { - Chart as ChartJS, - CategoryScale, - LinearScale, - BarElement, - LineElement, - PointElement, - ArcElement, - Title, - Tooltip, - Legend, - } from 'chart.js'; - import { Bar, Line, Pie, Scatter, Doughnut } from 'react-chartjs-2'; - - ChartJS.register( - CategoryScale, - LinearScale, - BarElement, - LineElement, - PointElement, - ArcElement, - Title, - Tooltip, - Legend - ); - - export interface ChartViewerProps { - data: ChartData; - type: 'bar' | 'line' | 'pie' | 'scatter' | 'doughnut'; - title?: string; - height?: number; - interactive?: boolean; - downloadable?: boolean; - } - - export function ChartViewer({ - data, - type, - title, - height = 400, - interactive = true, - downloadable = true, - }: ChartViewerProps) { - const chartRef = useRef(null); - const [isFullscreen, setIsFullscreen] = useState(false); - - const chartOptions = useMemo(() => ({ - responsive: true, - maintainAspectRatio: false, - interaction: { - enabled: interactive, - }, - plugins: { - legend: { - position: 'top' as const, - }, - title: { - display: !!title, - text: title, - }, - tooltip: { - enabled: interactive, - callbacks: { - label: (context: any) => { - return `${context.dataset.label}: ${context.parsed.y}`; - }, - }, - }, - }, - scales: type !== 'pie' && type !== 'doughnut' ? { - y: { - beginAtZero: true, - }, - } : {}, - }), [title, interactive, type]); - - const renderChart = () => { - switch (type) { - case 'bar': - return ; - case 'line': - return ; - case 'pie': - return ; - case 'scatter': - return ; - case 'doughnut': - return ; - default: - return null; - } - }; - - const handleDownload = () => { - if (chartRef.current) { - const canvas = chartRef.current.canvas; - const url = canvas.toDataURL('image/png'); - const link = document.createElement('a'); - link.download = `${title || 'chart'}.png`; - link.href = url; - link.click(); - } - }; - - return ( - - {/* Chart Controls */} - - - {downloadable && ( - - )} - - - - - {/* Chart Content */} - - {renderChart()} - - - {/* Fullscreen Modal */} - setIsFullscreen(false)} - maxWidth="lg" - fullWidth - > - - {title} - setIsFullscreen(false)} - sx={{ position: 'absolute', right: 8, top: 8 }} - > - - - - - {renderChart()} - - - - ); - } - ``` - -4. **Implement Universal Output Renderer:** - - Create intelligent output type detection and rendering: - ```typescript - // src/components/output/OutputRenderer.tsx - export interface OutputRendererProps { - data: any; - metadata?: OutputMetadata; - title?: string; - height?: number; - interactive?: boolean; - } - - export function OutputRenderer({ - data, - metadata, - title, - height = 400, - interactive = true, - }: OutputRendererProps) { - const outputType = useMemo(() => { - return detectOutputType(data, metadata); - }, [data, metadata]); - - const renderOutput = () => { - switch (outputType) { - case 'geojson': - return ( - - ); - - case 'table': - return ( - - ); - - case 'chart': - return ( - - ); - - case 'image': - return ( - - ); - - case 'text': - return ( - - ); - - case 'json': - return ( - - ); - - default: - return ( - - ); - } - }; - - return ( - - {title && ( - - - - - - } - /> - )} - - {renderOutput()} - - - ); - } - - // src/utils/outputTypeDetection.ts - export function detectOutputType(data: any, metadata?: OutputMetadata): OutputType { - // Explicit type from metadata - if (metadata?.type) { - return metadata.type; - } - - // GeoJSON detection - if (isGeoJSON(data)) { - return 'geojson'; - } - - // Table/array detection - if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') { - return 'table'; - } - - // Chart data detection - if (isChartData(data)) { - return 'chart'; - } - - // Image detection - if (typeof data === 'string' && isImageUrl(data)) { - return 'image'; - } - - // JSON object - if (typeof data === 'object' && data !== null) { - return 'json'; - } - - // Plain text - if (typeof data === 'string') { - return 'text'; - } - - return 'generic'; - } - ``` - -## 3. Specialized Viewers Implementation - -**Image Viewer with Zoom and Gallery:** -```typescript -// src/components/output/ImageViewer.tsx -export function ImageViewer({ - src, - alt, - maxHeight = 400, - zoomable = true -}: ImageViewerProps) { - const [isZoomed, setIsZoomed] = useState(false); - const [zoom, setZoom] = useState(1); - const [position, setPosition] = useState({ x: 0, y: 0 }); - - return ( - - {alt} zoomable && setIsZoomed(true)} - /> - - {/* Zoom Modal */} - setIsZoomed(false)} - maxWidth="lg" - fullWidth - > - setIsZoomed(false)} - /> - - - ); -} - -// src/components/output/TextViewer.tsx -export function TextViewer({ - content, - language, - maxHeight = 400, - searchable = true, -}: TextViewerProps) { - const [searchTerm, setSearchTerm] = useState(''); - const [highlightedContent, setHighlightedContent] = useState(content); - - useEffect(() => { - if (searchTerm && searchable) { - setHighlightedContent( - highlightSearchTerm(content, searchTerm) - ); - } else { - setHighlightedContent(content); - } - }, [content, searchTerm, searchable]); - - return ( - - {searchable && ( - setSearchTerm(e.target.value)} - sx={{ mb: 1 }} - /> - )} - - - {highlightedContent} - - - ); -} -``` - -## 4. Export and Sharing Capabilities - -**Universal Export System:** -```typescript -// src/utils/exportUtils.ts -export class OutputExporter { - static async exportMap(mapData: GeoJSON.FeatureCollection, format: 'geojson' | 'kml' | 'png'): Promise { - switch (format) { - case 'geojson': - this.downloadFile( - JSON.stringify(mapData, null, 2), - 'map-data.geojson', - 'application/geo+json' - ); - break; - - case 'kml': - const kml = this.convertGeoJSONToKML(mapData); - this.downloadFile(kml, 'map-data.kml', 'application/vnd.google-earth.kml+xml'); - break; - - case 'png': - // Export map as image using html2canvas - const mapElement = document.querySelector('.leaflet-container'); - if (mapElement) { - const canvas = await html2canvas(mapElement as HTMLElement); - canvas.toBlob(blob => { - if (blob) { - this.downloadBlob(blob, 'map-export.png'); - } - }); - } - break; - } - } - - static exportTable(data: any[], columns: GridColDef[], format: 'csv' | 'excel' | 'json'): void { - switch (format) { - case 'csv': - const csv = this.convertToCSV(data, columns); - this.downloadFile(csv, 'table-data.csv', 'text/csv'); - break; - - case 'excel': - const workbook = XLSX.utils.book_new(); - const worksheet = XLSX.utils.json_to_sheet(data); - XLSX.utils.book_append_sheet(workbook, worksheet, 'Data'); - XLSX.writeFile(workbook, 'table-data.xlsx'); - break; - - case 'json': - this.downloadFile( - JSON.stringify(data, null, 2), - 'table-data.json', - 'application/json' - ); - break; - } - } - - private static downloadFile(content: string, filename: string, mimeType: string): void { - const blob = new Blob([content], { type: mimeType }); - this.downloadBlob(blob, filename); - } - - private static downloadBlob(blob: Blob, filename: string): void { - const url = URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.download = filename; - link.click(); - URL.revokeObjectURL(url); - } -} -``` - -## 5. Performance Optimization - -**Virtualization for Large Datasets:** -```typescript -// src/components/output/VirtualizedOutput.tsx -export function VirtualizedOutput({ data, renderItem }: VirtualizedOutputProps) { - const virtuoso = useRef(null); - - return ( - renderItem(item, index)} - components={{ - Header: () => , - Footer: () => , - EmptyPlaceholder: () => , - }} - /> - ); -} - -// Lazy loading for heavy components -const LazyMapViewer = lazy(() => import('./MapViewer')); -const LazyChartViewer = lazy(() => import('./ChartViewer')); - -export function OptimizedOutputRenderer(props: OutputRendererProps) { - return ( - }> - - - ); -} -``` - -## 6. Expected Output & Deliverables - -**Success Criteria:** -- LeafletJS integration with interactive map features -- Comprehensive data table with sorting, filtering, and export -- Chart.js integration supporting multiple visualization types -- Universal output renderer with intelligent type detection -- Image viewer with zoom and gallery capabilities -- Text viewer with syntax highlighting and search -- Export functionality for all output types -- Performance optimization for large datasets - -**Deliverables:** -1. **Map Rendering:** - - `src/components/output/MapViewer.tsx` - Interactive map component - - `src/components/output/FeaturePopup.tsx` - Feature details - - `src/components/output/MapControls.tsx` - Map interaction controls - -2. **Data Visualization:** - - `src/components/output/TableViewer.tsx` - Interactive data table - - `src/components/output/ChartViewer.tsx` - Chart visualization - - `src/components/output/ImageViewer.tsx` - Image display and zoom - - `src/components/output/TextViewer.tsx` - Text with syntax highlighting - -3. **Universal Rendering:** - - `src/components/output/OutputRenderer.tsx` - Main renderer component - - `src/utils/outputTypeDetection.ts` - Type detection logic - - `src/utils/exportUtils.ts` - Export functionality - -4. **Performance & UX:** - - `src/components/output/VirtualizedOutput.tsx` - Large dataset handling - - `src/components/output/OutputSkeleton.tsx` - Loading states - - Lazy loading implementation - -## 7. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_4_Full_UI_Mock_Execution/Task_4.3_Output_Rendering_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_UI_Specialist) -- Task reference (Phase 4 / Task 4.3) -- Output rendering architecture -- Visualization libraries integrated -- Type detection and routing logic -- Export capabilities implemented -- Performance optimization strategies -- User interaction features - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_4.4_Tool_Execution.md b/prompts/tasks/Task_4.4_Tool_Execution.md deleted file mode 100644 index b30f902..0000000 --- a/prompts/tasks/Task_4.4_Tool_Execution.md +++ /dev/null @@ -1,768 +0,0 @@ -# APM Task Assignment: Tool Execution Integration - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 4, Task 4.4** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Wire tool execution flow from input to output display, connecting the ExecutionPanel with real functionality, workflow management, and comprehensive user feedback. - -**Prerequisites:** Tasks 4.1-4.3 completed - Mock backend service, enhanced search/filtering, and output rendering components should be operational. - -## 2. Detailed Action Steps - -1. **Enhance ExecutionPanel with Real Functionality:** - - Connect input forms to execution service: - ```typescript - // src/components/tools/EnhancedExecutionPanel.tsx - export interface ExecutionPanelProps { - tool: Tool; - onExecutionComplete?: (results: ExecutionResults) => void; - onExecutionError?: (error: ExecutionError) => void; - } - - export function EnhancedExecutionPanel({ - tool, - onExecutionComplete, - onExecutionError - }: ExecutionPanelProps) { - const [inputValues, setInputValues] = useState>({}); - const [validationErrors, setValidationErrors] = useState>({}); - const [execution, setExecution] = useState(null); - const [results, setResults] = useState(null); - - const { executeToool, getExecutionStatus, cancelExecution } = useToolExecution(); - const { showNotification } = useNotification(); - - // Real-time execution progress polling - useEffect(() => { - if (execution?.status === 'running') { - const interval = setInterval(async () => { - try { - const status = await getExecutionStatus(execution.id); - setExecution(status); - - if (status.status === 'completed') { - setResults(status.results); - onExecutionComplete?.(status.results); - showNotification('Tool execution completed successfully', 'success'); - } else if (status.status === 'failed') { - onExecutionError?.(status.error); - showNotification(status.error.message, 'error'); - } - } catch (error) { - console.error('Failed to get execution status:', error); - } - }, 1000); - - return () => clearInterval(interval); - } - }, [execution?.status, execution?.id]); - - const handleInputChange = (inputName: string, value: any) => { - setInputValues(prev => ({ ...prev, [inputName]: value })); - - // Clear validation error when user types - if (validationErrors[inputName]) { - setValidationErrors(prev => { - const newErrors = { ...prev }; - delete newErrors[inputName]; - return newErrors; - }); - } - }; - - const validateInputs = (): boolean => { - const errors: Record = {}; - - for (const input of tool.inputs) { - const value = inputValues[input.name]; - const validation = validateToolInput(input, value); - - if (!validation.isValid) { - errors[input.name] = validation.errors[input.name]; - } - } - - setValidationErrors(errors); - return Object.keys(errors).length === 0; - }; - - const handleExecute = async () => { - if (!validateInputs()) { - showNotification('Please fix validation errors before executing', 'warning'); - return; - } - - try { - const executionResponse = await executeToool(tool.id, inputValues); - setExecution({ - id: executionResponse.executionId, - status: 'running', - progress: 0, - startTime: new Date(), - }); - - showNotification('Tool execution started', 'info'); - } catch (error) { - onExecutionError?.(error as ExecutionError); - showNotification('Failed to start execution', 'error'); - } - }; - - const handleCancel = async () => { - if (execution && execution.status === 'running') { - try { - await cancelExecution(execution.id); - setExecution(prev => prev ? { ...prev, status: 'cancelled' } : null); - showNotification('Execution cancelled', 'info'); - } catch (error) { - showNotification('Failed to cancel execution', 'error'); - } - } - }; - - return ( - - - - {/* Input Forms */} - - - {/* Execution Controls */} - - - - {execution?.status === 'running' && ( - - )} - - - - - {/* Execution Progress */} - {execution && ( - - )} - - {/* Results Display */} - {results && ( - - - - Execution Results - - - - )} - - - ); - } - ``` - -2. **Create Execution Workflow Management:** - - Implement execution queuing and batch processing: - ```typescript - // src/services/executionWorkflowService.ts - export interface ExecutionWorkflow { - id: string; - name: string; - tools: ExecutionStep[]; - status: 'idle' | 'running' | 'completed' | 'failed' | 'paused'; - currentStep: number; - results: ExecutionStepResult[]; - } - - export interface ExecutionStep { - toolId: string; - inputs: Record; - dependsOn?: string[]; // Step IDs this step depends on - condition?: string; // Conditional execution logic - } - - export class ExecutionWorkflowService { - private workflows: Map = new Map(); - private executionQueue: ExecutionQueueItem[] = []; - private isProcessing = false; - - async createWorkflow( - name: string, - steps: ExecutionStep[] - ): Promise { - const workflow: ExecutionWorkflow = { - id: `workflow_${Date.now()}`, - name, - tools: steps, - status: 'idle', - currentStep: 0, - results: [], - }; - - this.workflows.set(workflow.id, workflow); - return workflow; - } - - async executeWorkflow(workflowId: string): Promise { - const workflow = this.workflows.get(workflowId); - if (!workflow) throw new Error('Workflow not found'); - - workflow.status = 'running'; - - try { - for (let i = 0; i < workflow.tools.length; i++) { - workflow.currentStep = i; - const step = workflow.tools[i]; - - // Check dependencies - if (step.dependsOn) { - const dependenciesMet = this.checkDependencies(step.dependsOn, workflow.results); - if (!dependenciesMet) { - throw new Error(`Dependencies not met for step ${i}`); - } - } - - // Execute step - const result = await this.executeStep(step, workflow.results); - workflow.results.push({ - stepIndex: i, - toolId: step.toolId, - ...result, - }); - } - - workflow.status = 'completed'; - } catch (error) { - workflow.status = 'failed'; - throw error; - } - } - - async executeStep( - step: ExecutionStep, - previousResults: ExecutionStepResult[] - ): Promise { - // Resolve dynamic inputs from previous results - const resolvedInputs = this.resolveInputs(step.inputs, previousResults); - - const response = await toolExecutionService.executeTool( - step.toolId, - resolvedInputs - ); - - // Poll for completion - let status = await toolExecutionService.getExecutionStatus(response.executionId); - while (status.status === 'running') { - await new Promise(resolve => setTimeout(resolve, 1000)); - status = await toolExecutionService.getExecutionStatus(response.executionId); - } - - if (status.status === 'failed') { - throw new Error(status.error.message); - } - - return status.results; - } - - private resolveInputs( - inputs: Record, - previousResults: ExecutionStepResult[] - ): Record { - const resolved = { ...inputs }; - - for (const [key, value] of Object.entries(resolved)) { - if (typeof value === 'string' && value.startsWith('${')) { - // Dynamic input resolution - const reference = value.slice(2, -1); // Remove ${ } - resolved[key] = this.resolveReference(reference, previousResults); - } - } - - return resolved; - } - } - ``` - -3. **Integrate Output Rendering with Execution:** - - Real-time output streaming and updates: - ```typescript - // src/components/tools/StreamingOutputRenderer.tsx - export function StreamingOutputRenderer({ - executionId, - onComplete, - onError, - }: StreamingOutputRendererProps) { - const [currentData, setCurrentData] = useState(null); - const [isStreaming, setIsStreaming] = useState(false); - const [progress, setProgress] = useState(0); - - useEffect(() => { - if (executionId) { - startStreaming(); - } - }, [executionId]); - - const startStreaming = async () => { - setIsStreaming(true); - - try { - // Server-sent events for real-time updates - const eventSource = new EventSource(`/api/executions/${executionId}/stream`); - - eventSource.onmessage = (event) => { - const data = JSON.parse(event.data); - - switch (data.type) { - case 'progress': - setProgress(data.progress); - break; - - case 'partial_result': - setCurrentData(prev => mergePartialData(prev, data.data)); - break; - - case 'complete': - setCurrentData(data.data); - setIsStreaming(false); - onComplete?.(data.data); - eventSource.close(); - break; - - case 'error': - setIsStreaming(false); - onError?.(data.error); - eventSource.close(); - break; - } - }; - - eventSource.onerror = () => { - setIsStreaming(false); - onError?.(new Error('Stream connection failed')); - eventSource.close(); - }; - } catch (error) { - setIsStreaming(false); - onError?.(error as Error); - } - }; - - return ( - - {isStreaming && ( - - - Streaming results... {Math.round(progress)}% - - - - )} - - {currentData && ( - - )} - - ); - } - ``` - -4. **Add Execution Analytics and Feedback:** - - Comprehensive execution tracking and user feedback: - ```typescript - // src/services/executionAnalyticsService.ts - export class ExecutionAnalyticsService { - private metrics: Map = new Map(); - - recordExecution( - toolId: string, - inputs: Record, - execution: ExecutionState - ): void { - const metric: ExecutionMetrics = { - toolId, - executionId: execution.id, - startTime: execution.startTime, - endTime: execution.endTime, - duration: execution.endTime - ? execution.endTime.getTime() - execution.startTime.getTime() - : null, - status: execution.status, - inputSize: this.calculateInputSize(inputs), - outputSize: execution.results ? this.calculateOutputSize(execution.results) : null, - errorType: execution.error?.code, - userId: this.getCurrentUserId(), - timestamp: new Date(), - }; - - const toolMetrics = this.metrics.get(toolId) || []; - toolMetrics.push(metric); - this.metrics.set(toolId, toolMetrics); - - // Send to analytics backend - this.sendToAnalytics(metric); - } - - getToolMetrics(toolId: string): ToolAnalytics { - const metrics = this.metrics.get(toolId) || []; - - return { - totalExecutions: metrics.length, - successRate: this.calculateSuccessRate(metrics), - averageDuration: this.calculateAverageDuration(metrics), - popularInputs: this.identifyPopularInputs(metrics), - errorPatterns: this.analyzeErrorPatterns(metrics), - usageOverTime: this.generateUsageTimeline(metrics), - }; - } - - generateExecutionReport(executionId: string): ExecutionReport { - // Implementation for detailed execution reports - return { - executionId, - summary: this.generateSummary(executionId), - performance: this.analyzePerformance(executionId), - recommendations: this.generateRecommendations(executionId), - }; - } - } - - // src/components/tools/ExecutionFeedback.tsx - export function ExecutionFeedback({ - execution, - onFeedbackSubmit, - }: ExecutionFeedbackProps) { - const [rating, setRating] = useState(null); - const [feedback, setFeedback] = useState(''); - const [reportIssue, setReportIssue] = useState(false); - - const handleSubmit = () => { - onFeedbackSubmit({ - executionId: execution.id, - rating, - feedback, - reportIssue, - timestamp: new Date(), - }); - }; - - return ( - - - - - - How would you rate this execution? - - setRating(value)} - size="large" - /> - - - setFeedback(e.target.value)} - fullWidth - sx={{ mb: 2 }} - /> - - setReportIssue(e.target.checked)} - /> - } - label="Report an issue with this execution" - /> - - - - - - - ); - } - ``` - -## 3. Advanced Execution Features - -**Execution Templates and Saved Configurations:** -```typescript -// src/components/tools/ExecutionTemplates.tsx -export function ExecutionTemplates({ tool }: ExecutionTemplatesProps) { - const [templates, setTemplates] = useState([]); - const [selectedTemplate, setSelectedTemplate] = useState(null); - - const saveTemplate = (name: string, inputs: Record) => { - const template: ExecutionTemplate = { - id: `template_${Date.now()}`, - name, - toolId: tool.id, - inputs, - createdAt: new Date(), - }; - - setTemplates(prev => [...prev, template]); - - // Persist to local storage - const saved = JSON.parse(localStorage.getItem('execution-templates') || '[]'); - saved.push(template); - localStorage.setItem('execution-templates', JSON.stringify(saved)); - }; - - return ( - - - - - {templates.map(template => ( - - - - {template.name} - - {new Date(template.createdAt).toLocaleDateString()} - - - - - - - - ))} - - - - ); -} -``` - -**Execution Comparison and Analysis:** -```typescript -// src/components/tools/ExecutionComparison.tsx -export function ExecutionComparison({ - executions, -}: ExecutionComparisonProps) { - const [selectedExecutions, setSelectedExecutions] = useState([]); - - const compareExecutions = () => { - const comparison = { - performance: comparePerformance(selectedExecutions), - outputs: compareOutputs(selectedExecutions), - inputs: compareInputs(selectedExecutions), - }; - - return comparison; - }; - - return ( - - - - - - {selectedExecutions.length >= 2 && ( - - )} - - - ); -} -``` - -## 4. Error Handling and Recovery - -**Advanced Error Handling:** -```typescript -// src/components/tools/ExecutionErrorHandler.tsx -export function ExecutionErrorHandler({ - error, - onRetry, - onReport, -}: ExecutionErrorHandlerProps) { - const getSuggestedActions = (error: ExecutionError) => { - switch (error.code) { - case 'INVALID_INPUT': - return [ - 'Check input validation requirements', - 'Review input format and types', - 'Try with sample data', - ]; - - case 'RESOURCE_EXHAUSTED': - return [ - 'Reduce input data size', - 'Split processing into smaller chunks', - 'Try again during off-peak hours', - ]; - - case 'TIMEOUT': - return [ - 'Reduce processing complexity', - 'Increase timeout settings', - 'Break into smaller operations', - ]; - - default: - return ['Contact support for assistance']; - } - }; - - return ( - - Execution Failed - - {error.message} - - - - }> - Suggested Actions - - - - {getSuggestedActions(error).map((action, index) => ( - - - - - - - ))} - - - - - - {error.retryable && ( - - )} - - - - ); -} -``` - -## 5. Expected Output & Deliverables - -**Success Criteria:** -- Functional ExecutionPanel connected to mock backend -- Real-time execution progress and status updates -- Comprehensive error handling with recovery suggestions -- Execution workflow management and queuing -- Output streaming and incremental updates -- Execution analytics and user feedback collection -- Template system for saved configurations -- Execution comparison and analysis tools - -**Deliverables:** -1. **Enhanced Execution Components:** - - `src/components/tools/EnhancedExecutionPanel.tsx` - - `src/components/tools/ExecutionProgress.tsx` - - `src/components/tools/StreamingOutputRenderer.tsx` - - `src/components/tools/ExecutionErrorHandler.tsx` - -2. **Workflow Management:** - - `src/services/executionWorkflowService.ts` - - `src/components/tools/ExecutionTemplates.tsx` - - `src/components/tools/ExecutionComparison.tsx` - -3. **Analytics and Feedback:** - - `src/services/executionAnalyticsService.ts` - - `src/components/tools/ExecutionFeedback.tsx` - - `src/components/tools/ExecutionMetrics.tsx` - -4. **Service Integration:** - - `src/hooks/useToolExecution.ts` - - Enhanced `src/services/toolExecutionService.ts` - - Real-time streaming support - -## 6. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_4_Full_UI_Mock_Execution/Task_4.4_Tool_Execution_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_Frontend_Dev) -- Task reference (Phase 4 / Task 4.4) -- Execution workflow architecture -- Real-time streaming implementation -- Error handling and recovery mechanisms -- Analytics and feedback systems -- Template and comparison features -- Integration with output rendering - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_4.5_Responsive_Design.md b/prompts/tasks/Task_4.5_Responsive_Design.md deleted file mode 100644 index 23c6907..0000000 --- a/prompts/tasks/Task_4.5_Responsive_Design.md +++ /dev/null @@ -1,765 +0,0 @@ -# APM Task Assignment: Responsive Design and Mobile Optimization - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to **Phase 4, Task 4.5** in the Implementation Plan (`Implementation_Plan.md`). - -**Objective:** Ensure optimal experience across all device types and screen sizes with comprehensive responsive design, mobile-specific interactions, performance optimization, and adaptive UI components. - -**Prerequisites:** Tasks 4.1-4.4 completed - Mock backend, enhanced search, output rendering, and tool execution should be fully functional. - -## 2. Detailed Action Steps - -1. **Implement Comprehensive Responsive Design:** - - Create responsive breakpoint system: - ```typescript - // src/theme/responsive.ts - export const breakpoints = { - xs: 0, // Mobile portrait - sm: 600, // Mobile landscape / small tablet - md: 900, // Tablet - lg: 1200, // Desktop - xl: 1536, // Large desktop - }; - - export const responsiveTheme = createTheme({ - breakpoints: { - values: breakpoints, - }, - components: { - MuiContainer: { - styleOverrides: { - root: { - '@media (max-width: 600px)': { - paddingLeft: 16, - paddingRight: 16, - }, - }, - }, - }, - MuiCard: { - styleOverrides: { - root: { - '@media (max-width: 600px)': { - margin: '8px 0', - borderRadius: 8, - }, - }, - }, - }, - }, - }); - - // Responsive hook for component logic - export function useResponsive() { - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - const isTablet = useMediaQuery(theme.breakpoints.between('sm', 'lg')); - const isDesktop = useMediaQuery(theme.breakpoints.up('lg')); - const isTouchDevice = useMediaQuery('(pointer: coarse)'); - - return { - isMobile, - isTablet, - isDesktop, - isTouchDevice, - breakpoint: isMobile ? 'mobile' : isTablet ? 'tablet' : 'desktop', - }; - } - ``` - - - Implement responsive layouts for all major components: - ```typescript - // src/components/layout/ResponsiveToolList.tsx - export function ResponsiveToolList({ tools }: ResponsiveToolListProps) { - const { isMobile, isTablet, isDesktop } = useResponsive(); - const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid'); - - const getGridColumns = () => { - if (isMobile) return 1; - if (isTablet) return 2; - return 3; - }; - - const getCardHeight = () => { - if (isMobile) return 'auto'; - return 280; - }; - - return ( - - {/* View Mode Toggle - Hidden on mobile */} - {!isMobile && ( - - newMode && setViewMode(newMode)} - size="small" - > - - - - - - - - - )} - - {/* Responsive Grid */} - - {tools.map((tool) => ( - - - - ))} - - - ); - } - ``` - -2. **Create Mobile-Specific Interactions:** - - Implement touch-friendly controls and gestures: - ```typescript - // src/components/mobile/TouchInteractions.tsx - export function TouchInteractions({ children }: TouchInteractionsProps) { - const { isTouchDevice } = useResponsive(); - - if (!isTouchDevice) return <>{children}; - - return ( - - {children} - - ); - } - - // src/hooks/useSwipeGestures.ts - export function useSwipeGestures( - onSwipeLeft?: () => void, - onSwipeRight?: () => void, - threshold = 50 - ) { - const [touchStart, setTouchStart] = useState(null); - const [touchEnd, setTouchEnd] = useState(null); - - const onTouchStart = (e: TouchEvent) => { - setTouchEnd(null); - setTouchStart(e.targetTouches[0].clientX); - }; - - const onTouchMove = (e: TouchEvent) => { - setTouchEnd(e.targetTouches[0].clientX); - }; - - const onTouchEnd = () => { - if (!touchStart || !touchEnd) return; - - const distance = touchStart - touchEnd; - const isLeftSwipe = distance > threshold; - const isRightSwipe = distance < -threshold; - - if (isLeftSwipe && onSwipeLeft) { - onSwipeLeft(); - } - if (isRightSwipe && onSwipeRight) { - onSwipeRight(); - } - }; - - return { - onTouchStart, - onTouchMove, - onTouchEnd, - }; - } - - // src/components/mobile/SwipeableToolCard.tsx - export function SwipeableToolCard({ tool, onView, onBookmark }: SwipeableToolCardProps) { - const swipeHandlers = useSwipeGestures( - () => onBookmark?.(tool), // Swipe left to bookmark - () => onView(tool) // Swipe right to view - ); - - return ( - - - {/* Card content */} - - - {/* Swipe Indicators */} - - - ← View - - - Bookmark → - - - - ); - } - ``` - -3. **Optimize Performance for Mobile Devices:** - - Implement connection-aware features and lazy loading: - ```typescript - // src/hooks/useConnectionAware.ts - export function useConnectionAware() { - const [connectionType, setConnectionType] = useState('unknown'); - const [saveData, setSaveData] = useState(false); - const [isSlowConnection, setIsSlowConnection] = useState(false); - - useEffect(() => { - if ('connection' in navigator) { - const connection = (navigator as any).connection; - - const updateConnection = () => { - setConnectionType(connection.effectiveType); - setSaveData(connection.saveData); - setIsSlowConnection(['slow-2g', '2g'].includes(connection.effectiveType)); - }; - - updateConnection(); - connection.addEventListener('change', updateConnection); - - return () => { - connection.removeEventListener('change', updateConnection); - }; - } - }, []); - - return { - connectionType, - saveData, - isSlowConnection, - shouldOptimize: saveData || isSlowConnection, - }; - } - - // src/components/mobile/AdaptiveImageLoader.tsx - export function AdaptiveImageLoader({ - src, - alt, - lowQualitySrc, - ...props - }: AdaptiveImageLoaderProps) { - const { shouldOptimize } = useConnectionAware(); - const [imageLoaded, setImageLoaded] = useState(false); - const [currentSrc, setCurrentSrc] = useState( - shouldOptimize && lowQualitySrc ? lowQualitySrc : src - ); - - useEffect(() => { - if (shouldOptimize && lowQualitySrc && !imageLoaded) { - // Load low quality first - setCurrentSrc(lowQualitySrc); - - // Then load high quality - const highQualityImage = new Image(); - highQualityImage.onload = () => { - setCurrentSrc(src); - setImageLoaded(true); - }; - highQualityImage.src = src; - } - }, [src, lowQualitySrc, shouldOptimize, imageLoaded]); - - return ( - {alt} - ); - } - - // src/components/mobile/ProgressiveToolList.tsx - export function ProgressiveToolList({ tools }: ProgressiveToolListProps) { - const { shouldOptimize } = useConnectionAware(); - const [visibleCount, setVisibleCount] = useState(shouldOptimize ? 5 : 12); - const [isLoading, setIsLoading] = useState(false); - - const loadMore = useCallback(async () => { - setIsLoading(true); - // Simulate network delay for demonstration - await new Promise(resolve => setTimeout(resolve, 500)); - setVisibleCount(prev => prev + (shouldOptimize ? 3 : 8)); - setIsLoading(false); - }, [shouldOptimize]); - - return ( - - - - {visibleCount < tools.length && ( - - - - )} - - ); - } - ``` - -4. **Create Adaptive UI Components:** - - Build components that adapt to screen size and device capabilities: - ```typescript - // src/components/adaptive/AdaptiveNavigation.tsx - export function AdaptiveNavigation() { - const { isMobile, isTablet } = useResponsive(); - const [mobileOpen, setMobileOpen] = useState(false); - - const handleDrawerToggle = () => { - setMobileOpen(prev => !prev); - }; - - if (isMobile) { - return ( - <> - - - - - - - ToolVault - - - - - - - - - ); - } - - return ( - - - - ); - } - - // src/components/adaptive/AdaptiveToolDetail.tsx - export function AdaptiveToolDetail({ tool }: AdaptiveToolDetailProps) { - const { isMobile } = useResponsive(); - const [activeTab, setActiveTab] = useState(0); - - if (isMobile) { - return ( - - setActiveTab(newValue)} - variant="fullWidth" - > - - - - - - - - - - - - - - - - ); - } - - return ( - - - - - - - - - - - - ); - } - - // src/components/adaptive/AdaptiveSearch.tsx - export function AdaptiveSearch({ onSearch, onFilter }: AdaptiveSearchProps) { - const { isMobile } = useResponsive(); - const [filtersOpen, setFiltersOpen] = useState(false); - - if (isMobile) { - return ( - - - onSearch(e.target.value)} - InputProps={{ - startAdornment: , - }} - /> - setFiltersOpen(true)} - sx={{ minWidth: 48 }} - > - - - - - - setFiltersOpen(false)} - onOpen={() => setFiltersOpen(true)} - disableSwipeToOpen={false} - PaperProps={{ - sx: { maxHeight: '70vh', borderRadius: '16px 16px 0 0' } - }} - > - setFiltersOpen(false)} - /> - - - ); - } - - return ( - - onSearch(e.target.value)} - sx={{ flexGrow: 1 }} - InputProps={{ - startAdornment: , - }} - /> - - - ); - } - ``` - -## 3. Advanced Mobile Features - -**Offline Support and PWA Features:** -```typescript -// src/hooks/useOfflineSupport.ts -export function useOfflineSupport() { - const [isOnline, setIsOnline] = useState(navigator.onLine); - const [offlineData, setOfflineData] = useState([]); - - useEffect(() => { - const handleOnline = () => setIsOnline(true); - const handleOffline = () => setIsOnline(false); - - window.addEventListener('online', handleOnline); - window.addEventListener('offline', handleOffline); - - return () => { - window.removeEventListener('online', handleOnline); - window.removeEventListener('offline', handleOffline); - }; - }, []); - - const cacheData = useCallback((key: string, data: any) => { - localStorage.setItem(`offline_${key}`, JSON.stringify({ - data, - timestamp: Date.now(), - })); - }, []); - - const getCachedData = useCallback((key: string, maxAge = 24 * 60 * 60 * 1000) => { - try { - const cached = localStorage.getItem(`offline_${key}`); - if (cached) { - const { data, timestamp } = JSON.parse(cached); - if (Date.now() - timestamp < maxAge) { - return data; - } - } - } catch (error) { - console.error('Error retrieving cached data:', error); - } - return null; - }, []); - - return { - isOnline, - cacheData, - getCachedData, - offlineData, - }; -} - -// src/components/mobile/PWAInstallPrompt.tsx -export function PWAInstallPrompt() { - const [deferredPrompt, setDeferredPrompt] = useState(null); - const [showPrompt, setShowPrompt] = useState(false); - - useEffect(() => { - const handleBeforeInstallPrompt = (e: Event) => { - e.preventDefault(); - setDeferredPrompt(e); - setShowPrompt(true); - }; - - window.addEventListener('beforeinstallprompt', handleBeforeInstallPrompt); - - return () => { - window.removeEventListener('beforeinstallprompt', handleBeforeInstallPrompt); - }; - }, []); - - const handleInstall = async () => { - if (deferredPrompt) { - deferredPrompt.prompt(); - const { outcome } = await deferredPrompt.userChoice; - - if (outcome === 'accepted') { - console.log('PWA installed'); - } - - setDeferredPrompt(null); - setShowPrompt(false); - } - }; - - if (!showPrompt) return null; - - return ( - setShowPrompt(false)} - > - - Install App - - } - onClose={() => setShowPrompt(false)} - > - Install ToolVault for quick access and offline use - - - ); -} -``` - -**Mobile-Specific Performance Optimizations:** -```typescript -// src/hooks/useViewportOptimization.ts -export function useViewportOptimization() { - const [viewportHeight, setViewportHeight] = useState(window.innerHeight); - const { isMobile } = useResponsive(); - - useEffect(() => { - if (isMobile) { - // Handle mobile viewport changes (keyboard appearance) - const handleResize = () => { - const vh = window.innerHeight * 0.01; - document.documentElement.style.setProperty('--vh', `${vh}px`); - setViewportHeight(window.innerHeight); - }; - - handleResize(); - window.addEventListener('resize', handleResize); - - return () => window.removeEventListener('resize', handleResize); - } - }, [isMobile]); - - return { viewportHeight }; -} - -// src/components/mobile/VirtualKeyboardHandler.tsx -export function VirtualKeyboardHandler({ children }: VirtualKeyboardHandlerProps) { - const { isMobile } = useResponsive(); - const [keyboardOpen, setKeyboardOpen] = useState(false); - - useEffect(() => { - if (isMobile && 'visualViewport' in window) { - const viewport = window.visualViewport!; - - const handleViewportChange = () => { - const keyboardHeight = window.innerHeight - viewport.height; - setKeyboardOpen(keyboardHeight > 100); - }; - - viewport.addEventListener('resize', handleViewportChange); - return () => viewport.removeEventListener('resize', handleViewportChange); - } - }, [isMobile]); - - return ( - - {children} - - ); -} -``` - -## 4. Expected Output & Deliverables - -**Success Criteria:** -- Seamless experience across desktop (>1200px), tablet (768-1199px), and mobile (<768px) -- Touch-friendly interactions with appropriate target sizes -- Swipe gestures for mobile navigation -- Connection-aware loading and data optimization -- Adaptive UI components that change based on screen size -- PWA capabilities with offline support -- Performance optimized for mobile networks -- Virtual keyboard handling - -**Deliverables:** -1. **Responsive Infrastructure:** - - `src/theme/responsive.ts` - Responsive theme and breakpoints - - `src/hooks/useResponsive.ts` - Responsive utilities hook - - `src/hooks/useConnectionAware.ts` - Connection awareness - - `src/hooks/useOfflineSupport.ts` - Offline capabilities - -2. **Mobile Components:** - - `src/components/mobile/TouchInteractions.tsx` - Touch enhancements - - `src/components/mobile/SwipeableToolCard.tsx` - Swipe gestures - - `src/components/mobile/AdaptiveImageLoader.tsx` - Performance optimization - - `src/components/mobile/PWAInstallPrompt.tsx` - PWA installation - -3. **Adaptive Layouts:** - - `src/components/layout/ResponsiveToolList.tsx` - Responsive tool grid - - `src/components/adaptive/AdaptiveNavigation.tsx` - Responsive navigation - - `src/components/adaptive/AdaptiveToolDetail.tsx` - Mobile-friendly detail view - - `src/components/adaptive/AdaptiveSearch.tsx` - Responsive search interface - -4. **Performance Features:** - - `src/components/mobile/ProgressiveToolList.tsx` - Progressive loading - - `src/components/mobile/VirtualKeyboardHandler.tsx` - Keyboard optimization - - `src/hooks/useViewportOptimization.ts` - Viewport utilities - - Connection-aware feature loading - -## 5. Memory Bank Logging Instructions - -**Instruction:** Upon successful completion, log your work to: -`Memory/Phase_4_Full_UI_Mock_Execution/Task_4.5_Responsive_Design_Log.md` - -**Format:** Follow `prompts/02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md`. Include: -- Agent identifier (Agent_UI_Specialist) -- Task reference (Phase 4 / Task 4.5) -- Responsive design strategy and breakpoints -- Mobile interaction patterns implemented -- Performance optimization techniques -- PWA features and offline support -- Adaptive component architecture -- Cross-device testing results - -Please acknowledge receipt and proceed with implementation. \ No newline at end of file diff --git a/prompts/tasks/Task_5.1_Integrate_Real_Tool_Modules.md b/prompts/tasks/Task_5.1_Integrate_Real_Tool_Modules.md deleted file mode 100644 index d065ee1..0000000 --- a/prompts/tasks/Task_5.1_Integrate_Real_Tool_Modules.md +++ /dev/null @@ -1,49 +0,0 @@ -# APM Task Assignment: Integrate Real Tool Modules (TypeScript/JavaScript) - -## 1. Agent Role & APM Context - -You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault Phase 1 project. Your role is to execute the assigned integration of real tool modules (TypeScript/JavaScript) into the SPA, following the Implementation Plan, and to log your work meticulously in the Memory Bank. - -## 2. Task Assignment - -This assignment corresponds to **Phase 5, Task 5.1** in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** -Enable execution of real tools written in TypeScript/JavaScript, compiled as ES modules, within the SPA (no Python infrastructure). - -**Detailed Action Steps:** -1. Define the tool module structure and requirements: - - Specify a convention for tool modules: each must export a `run()` function with a well-defined input/output signature. - - Document the expected input/output types and error handling approach. - - Reference the sample in `index.json` and the Software Requirements Document (SRD) for structure and contract. -2. Organize tool source and build outputs: - - Place tool `.ts` source and compiled `.js` files in `client/src/tools/` or the designated directory. - - Ensure the build process outputs ES modules compatible with dynamic import. - - Use Vite/TypeScript build config to ensure correct output format. -3. Update `index.json` with real tool entries: - - Add at least two tools (e.g., `word-count`, `change-color-to-red`) with correct module paths. - - Specify params and outputs as per SRD example. - - Validate the index against the schema. - -**Guidance:** -- Pay close attention to the 'Guidance:' notes in the Implementation Plan for each action step. -- Ensure all conventions and contracts are clear and documented for future tool contributors. - -## 3. Expected Output & Deliverables -- Documented module structure and requirements for tools. -- At least two working tool modules in `client/src/tools/`, with both source and compiled outputs. -- Updated `index.json` with valid entries for each tool. -- All outputs must conform to the schema and be ready for dynamic import. - -## 4. Memory Bank Logging Instructions -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to the assigned task in the Implementation Plan. -- A clear description of the actions taken. -- Any code snippets generated or modified. -- Any key decisions made or challenges encountered. -- Confirmation of successful execution (e.g., tests passing, output generated). - -If a dedicated [Memory_Bank_Log_Format.md](../02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md) file exists, reference it directly. - -## 5. Clarification Instruction -If any part of this task assignment is unclear, please state your specific questions before proceeding. diff --git a/prompts/tasks/Task_5.2_Dynamic_Tool_Loading_Execution.md b/prompts/tasks/Task_5.2_Dynamic_Tool_Loading_Execution.md deleted file mode 100644 index b7cab38..0000000 --- a/prompts/tasks/Task_5.2_Dynamic_Tool_Loading_Execution.md +++ /dev/null @@ -1,55 +0,0 @@ -# APM Task Assignment: Dynamic Tool Loading and Execution Pipeline - -## 1. Agent Role & APM Context - -You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault Phase 1 project. Your responsibility is to implement the dynamic tool loading and execution pipeline for real tool modules, ensuring robust input validation and output rendering in the SPA. - -## 2. Task Assignment - -This assignment corresponds to **Phase 5, Task 5.2** in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** -Implement dynamic loading and execution of tool modules via `import()`, with input validation and output rendering. - -**Detailed Action Steps:** -1. Implement dynamic import of tool modules: - - Use `import()` to load the module specified in `index.json` at runtime. - - Handle loading errors and provide graceful fallbacks in the UI. - - Ensure compatibility with the Vite/SPA build pipeline. -2. Validate tool inputs and outputs: - - Validate input parameters against type definitions in `index.json` before execution. - - Validate outputs after execution; display an error if the contract is violated. - - Use TypeScript types and runtime checks for validation. -3. Invoke `run()` in a Web Worker: - - Offload execution to a Web Worker to avoid blocking the UI. - - Pass validated inputs to the worker and handle outputs/messages from the worker. - - Handle worker errors and timeouts appropriately. - - Use transferable objects for performance if possible. -4. Render outputs in the UI: - - Display results using the existing output rendering pipeline (tables, maps, etc.). - - Show execution progress and errors. - - Reuse or extend UI components from previous phases as needed. - -**Guidance:** -- Carefully follow the 'Guidance:' notes in the Implementation Plan for each step. -- Ensure robust error handling and clear user/developer feedback. - -## 3. Expected Output & Deliverables -- Dynamic tool loading and execution pipeline implemented in the SPA. -- Input and output validation logic integrated with the UI. -- Web Worker-based execution for tools. -- Output rendering integrated with the pipeline. -- All changes documented and tested as appropriate. - -## 4. Memory Bank Logging Instructions -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to the assigned task in the Implementation Plan. -- A clear description of the actions taken. -- Any code snippets generated or modified. -- Any key decisions made or challenges encountered. -- Confirmation of successful execution (e.g., tests passing, output generated). - -If a dedicated [Memory_Bank_Log_Format.md](../02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md) file exists, reference it directly. - -## 5. Clarification Instruction -If any part of this task assignment is unclear, please state your specific questions before proceeding. diff --git a/prompts/tasks/Task_5.3_Error_Handling_Dev_Experience.md b/prompts/tasks/Task_5.3_Error_Handling_Dev_Experience.md deleted file mode 100644 index 4acb5a5..0000000 --- a/prompts/tasks/Task_5.3_Error_Handling_Dev_Experience.md +++ /dev/null @@ -1,49 +0,0 @@ -# APM Task Assignment: Error Handling and Developer Experience for Tool Integration - -## 1. Agent Role & APM Context - -You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault Phase 1 project. Your responsibility is to implement robust error handling and developer experience improvements for the tool integration and execution pipeline, as described in the Implementation Plan. - -## 2. Task Assignment - -This assignment corresponds to **Phase 5, Task 5.3** in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** -Provide robust error handling, developer feedback, and maintainability for tool integration and execution. - -**Detailed Action Steps:** -1. Implement user-friendly error reporting for tool execution failures: - - Display clear error messages for module load errors, validation failures, and runtime exceptions. - - Provide developer diagnostics in development mode. - - Ensure errors are actionable for both users and developers. -2. Document the tool integration process: - - Create a developer guide for adding new tools (structure, build, registration in `index.json`). - - Include troubleshooting tips for common issues (e.g., module not found, type mismatch). - - Update project docs in `client/docs/` as needed. -3. Add basic test cases for the tool execution pipeline: - - Write unit tests for dynamic import, validation, and worker communication. - - Ensure at least one test per tool type (e.g., GeoJSON, string processing). - - Use Jest/RTL for UI, and worker test utilities as appropriate. - -**Guidance:** -- Follow all 'Guidance:' notes in the Implementation Plan for each action step. -- Ensure documentation is clear and actionable for future contributors. - -## 3. Expected Output & Deliverables -- User-facing and developer-facing error handling for tool execution pipeline. -- Developer guide for tool integration and troubleshooting. -- Basic unit tests for pipeline and tool types. -- All changes documented and tested as appropriate. - -## 4. Memory Bank Logging Instructions -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to the assigned task in the Implementation Plan. -- A clear description of the actions taken. -- Any code snippets generated or modified. -- Any key decisions made or challenges encountered. -- Confirmation of successful execution (e.g., tests passing, output generated). - -If a dedicated [Memory_Bank_Log_Format.md](../02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md) file exists, reference it directly. - -## 5. Clarification Instruction -If any part of this task assignment is unclear, please state your specific questions before proceeding. diff --git a/prompts/tasks/Task_5.4_Minimal_Catalog_Example_Tools.md b/prompts/tasks/Task_5.4_Minimal_Catalog_Example_Tools.md deleted file mode 100644 index b556cc7..0000000 --- a/prompts/tasks/Task_5.4_Minimal_Catalog_Example_Tools.md +++ /dev/null @@ -1,50 +0,0 @@ -# APM Task Assignment: Minimal Catalog and Example Tools - -## 1. Agent Role & APM Context - -You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault Phase 1 project. Your responsibility is to deliver a minimal working catalog with at least two real tools, fully integrated and executable via the SPA, as described in the Implementation Plan. - -## 2. Task Assignment - -This assignment corresponds to **Phase 5, Task 5.4** in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** -Deliver a minimal working catalog with at least two real tools, fully integrated and executable via the SPA. - -**Detailed Action Steps:** -1. Implement and test sample tools (`word-count`, `change-color-to-red`): - - Write TypeScript source, compile to ES modules, and register in `index.json`. - - Ensure tools conform to input/output contract and are documented. - - Follow the SRD sample for tool structure. -2. Verify end-to-end execution in the browser: - - Run tools via SPA UI, validate correct outputs and error handling. - - Test both tools with edge-case inputs. -3. Deliverables: - - Minimal tool catalog in `index.json`. - - At least two working tools in `client/src/tools/`. - - Worker-based execution pipeline. - - Basic error handling and tests. - -**Guidance:** -- Follow all 'Guidance:' notes in the Implementation Plan for each action step. -- Ensure all deliverables are fully functional and documented. - -## 3. Expected Output & Deliverables -- At least two working tools (e.g., `word-count`, `change-color-to-red`) in `client/src/tools/`. -- Minimal tool catalog in `index.json`. -- End-to-end execution pipeline validated in the browser. -- Worker-based execution and error handling in place. -- All changes documented and tested as appropriate. - -## 4. Memory Bank Logging Instructions -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to the assigned task in the Implementation Plan. -- A clear description of the actions taken. -- Any code snippets generated or modified. -- Any key decisions made or challenges encountered. -- Confirmation of successful execution (e.g., tests passing, output generated). - -If a dedicated [Memory_Bank_Log_Format.md](../02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md) file exists, reference it directly. - -## 5. Clarification Instruction -If any part of this task assignment is unclear, please state your specific questions before proceeding. diff --git a/prompts/tasks/Task_5.5_Additional_JS_Tools.md b/prompts/tasks/Task_5.5_Additional_JS_Tools.md deleted file mode 100644 index 0a6fc60..0000000 --- a/prompts/tasks/Task_5.5_Additional_JS_Tools.md +++ /dev/null @@ -1,51 +0,0 @@ -# APM Task Assignment: Implement Additional JS Tools for UI Demonstration - -## 1. Agent Role & APM Context - -You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault Phase 1 project. Your responsibility is to implement and integrate additional JavaScript tools to drive and demonstrate the user interface, as described in the software requirements and Implementation Plan. - -## 2. Task Assignment - -This assignment corresponds to **Phase 5, Task 5.5** in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** -Implement and integrate a set of additional JavaScript tools to drive and demonstrate the user interface. - -**Detailed Action Steps:** -1. Implement the following tools as TypeScript modules, compile to ES modules, and register in `index.json`: - - `word-frequency`: Accepts a paragraph of text, outputs a table of word frequencies (word and count). - - `flip-line`: Accepts a GeoJSON LineString, swaps the order of the latitude coordinates, leaving longitude unchanged. - - `line-centre`: Accepts a GeoJSON LineString, outputs a GeoJSON Point at the centre of the line (arithmetic mean, not spatial). - - `calc-speeds`: Accepts a GeoJSON LineString with a `properties.times` array of unix DTG (one per point), computes speed for each segment (distance/time), outputs a time-series array of speeds. -2. For each tool: - - Ensure input/output types are minimal but descriptive, and update `index.json` accordingly. - - Follow the same module structure and conventions as previous tool tasks (Task 5.1, 5.4). - - Validate inputs and outputs per the schema and UI contract. - - Add documentation for each tool, including usage and input/output contract. - - Provide at least one test case per tool. -3. Integrate these tools into the SPA so they are discoverable and executable via the UI. - - Ensure outputs render correctly using the existing output rendering pipeline. - - Validate correct error handling and UI feedback for edge cases and invalid input. - -**Guidance:** -Refer to the Software Requirements Document (SRD) and the sample tool entries in `index.json` for structure and contract. Tools should help drive development and demonstration of the interface, so prioritize clarity, robust input validation, and output rendering. - -## 3. Expected Output & Deliverables -- All four tools implemented as TypeScript modules and compiled to ES modules. -- Corresponding entries in `index.json` with minimal, descriptive input/output types. -- Documentation for each tool (usage, contract, test case). -- Integration into the SPA with discoverable and executable UI. -- Outputs rendered correctly and error handling validated. - -## 4. Memory Bank Logging Instructions -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to the assigned task in the Implementation Plan. -- A clear description of the actions taken. -- Any code snippets generated or modified. -- Any key decisions made or challenges encountered. -- Confirmation of successful execution (e.g., tests passing, output generated). - -If a dedicated [Memory_Bank_Log_Format.md](../02_Utility_Prompts_And_Format_Definitions/Memory_Bank_Log_Format.md) file exists, reference it directly. - -## 5. Clarification Instruction -If any part of this task assignment is unclear, please state your specific questions before proceeding. diff --git a/prompts/tasks/Task_Issue_1.md b/prompts/tasks/Task_Issue_1.md deleted file mode 100644 index 1149da4..0000000 --- a/prompts/tasks/Task_Issue_1.md +++ /dev/null @@ -1,183 +0,0 @@ -# APM Task Assignment: Implement Mock JavaScript Tool Bundle (Issue #1) - -## 1. Agent Role & APM Context - -You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your role is to execute the assigned task diligently and document your work comprehensively. You will implement a complete JavaScript-based tool bundle that enables UI development and testing without backend dependencies. - -## 2. Task Assignment - -### Reference to GitHub Issue -This assignment corresponds to GitHub Issue #1: "Implement Mock JavaScript Tool Bundle" -- Issue URL: https://github.com/debrief/ToolVault/issues/1 -- Priority: Medium (Enhancement) - -### Objective -Create a fully functional JavaScript tool bundle at `/examples/javascript-bundle/` containing 11 GeoJSON processing tools that execute entirely in the browser, following the specifications in ADR-013. - -### Detailed Action Steps - -#### Phase 1: Setup and Infrastructure -1. **Create the bundle directory structure** at `/examples/javascript-bundle/`: - - Create `tools/` subdirectories: `transform/`, `analysis/`, `statistics/`, `processing/`, `io/` - - Create `data/` directory for sample files - - Create `tests/` directory for Jest unit tests - -2. **Initialize the JavaScript project**: - - Create `package.json` with Jest testing framework - - Configure Jest for browser-compatible JavaScript testing - - Add npm scripts: `"test": "jest"`, `"test:watch": "jest --watch"` - -3. **Create the index.json metadata file**: - - Include `runtime: "javascript"` field for each tool - - Define all 11 tools with complete metadata - - Follow the schema established in ADR-013 - -4. **Generate sample data**: - - Create `data/sample-track.geojson` - a GPS track LineString with timestamp properties - - Ensure timestamps are suitable for speed/direction calculations - -#### Phase 2: Tool Implementation - -**CRITICAL: All tools MUST follow the IIFE pattern specified in ADR-013:** -```javascript -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.toolName = function(input, params) { - // Implementation - return output; - }; -})(); -``` - -**Transform Tools** (`tools/transform/`): -1. **translate.js** - Translate GeoJSON features - - Inputs: GeoJSON FeatureCollection, direction (degrees), distance (meters) - - Calculate new coordinates using bearing and distance formulas - -2. **flip-horizontal.js** - Flip features horizontally - - Input: GeoJSON FeatureCollection - - Mirror coordinates across specified axis - -3. **flip-vertical.js** - Flip features vertically - - Input: GeoJSON FeatureCollection - - Mirror coordinates across specified axis - -**Analysis Tools** (`tools/analysis/`): -4. **speed-series.js** - Calculate speed time series - - Input: GeoJSON LineString with timestamps - - Output: JSON array with time/speed pairs - - Use Haversine formula for distance calculations - -5. **direction-series.js** - Calculate direction time series - - Input: GeoJSON LineString with timestamps - - Output: JSON array with time/bearing pairs - - Calculate bearing between consecutive points - -**Statistics Tools** (`tools/statistics/`): -6. **average-speed.js** - Calculate average speed - - Input: GeoJSON LineString with timestamps - - Output: Single numeric value - -7. **speed-histogram.js** - Generate speed histogram - - Input: GeoJSON LineString with timestamps - - Parameters: interval_minutes (default: 1), bins (default: 20) - - Output: JSON histogram structure - -**Processing Tools** (`tools/processing/`): -8. **smooth-polyline.js** - Smooth LineString geometry - - Input: GeoJSON LineString - - Parameters: algorithm ("moving_average" or "gaussian"), window_size - - Apply smoothing to coordinate sequence - -**I/O Tools** (`tools/io/`): -9. **import-rep.js** - Parse REP format to GeoJSON - - Input: REP format text - - Output: GeoJSON FeatureCollection - -10. **export-rep.js** - Convert GeoJSON to REP format - - Input: GeoJSON FeatureCollection - - Output: REP format string - -11. **export-csv.js** - Convert GeoJSON to CSV - - Input: GeoJSON FeatureCollection - - Parameters: include_properties, coordinate_format - - Output: CSV string - -#### Phase 3: Testing - -For each tool, create a corresponding test file in `tests/`: -- Use Jest's `describe` and `test` blocks -- Test with the sample GPS track data -- Verify output structure and calculations -- Ensure all tests pass before marking complete - -Example test structure: -```javascript -describe('translateFeatures', () => { - test('should translate features by specified distance and direction', () => { - const input = // sample GeoJSON - const params = { direction: 45, distance: 100 }; - const result = window.ToolVault.tools.translateFeatures(input, params); - expect(result.type).toBe('FeatureCollection'); - // Additional assertions - }); -}); -``` - -### Provide Necessary Context/Assets - -**Reference ADR-013**: Review `/docs/ADRs/ADR-013-mock-javascript-toolset.md` for: -- Complete tool specifications table -- IIFE implementation pattern -- Input/output type definitions -- Bundle structure requirements - -**Technical Constraints**: -- No external dependencies beyond standard Web APIs -- All tools must be synchronous (no async/await) -- Use browser-compatible JavaScript (ES2020+) -- Minimal error handling (assume valid input) - -## 3. Expected Output & Deliverables - -### Success Criteria -- All 11 tools implemented and functional in browser environment -- Jest tests passing for all tools -- index.json contains complete metadata with runtime field -- Sample GPS track data available for testing -- Tools accessible via `window.ToolVault.tools` namespace - -### Deliverables -1. Complete `/examples/javascript-bundle/` directory with: - - Implemented tool files in appropriate subdirectories - - index.json with full metadata - - package.json with Jest configuration - - Sample data file(s) - - Comprehensive test suite - -2. All acceptance criteria from Issue #1 checked off: - - [ ] All 11 tools implemented with IIFE pattern - - [ ] Jest unit tests for each tool with passing status - - [ ] index.json with complete metadata and runtime field - - [ ] Sample GPS track data (single LineString with timestamps) - - [ ] Tools executable in browser environment - -## 4. Memory Bank Logging Instructions - -Upon successful completion of this task, you **must** log your work comprehensively to the project's Memory Bank (if established) or create a detailed implementation summary. Your log should include: -- Reference to GitHub Issue #1 -- List of all files created with their purposes -- Key implementation decisions (e.g., distance calculation methods, coordinate transformation approaches) -- Any challenges encountered and how they were resolved -- Confirmation of all tests passing -- Next steps for GitHub Pages deployment - -## 5. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. Pay particular attention to: -- REP file format specification (if not documented) -- Specific algorithms for smoothing functions -- Coordinate system assumptions (WGS84, etc.) -- Expected precision for calculations \ No newline at end of file From e98090cda85bc0c3bb383150703e75f28b35edcc Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 14:55:21 +0100 Subject: [PATCH 06/18] docs: create initial task assignments for Phase 0 implementation --- ...sk_0.1_Project_Structure_and_Jest_Setup.md | 70 ++++++++++++++ ...Task_0.2_Transform_Tools_Implementation.md | 80 ++++++++++++++++ .../tasks/Task_0.3_Transform_Tools_Testing.md | 86 +++++++++++++++++ .../Task_0.4_Analysis_Tools_Implementation.md | 89 ++++++++++++++++++ .../tasks/Task_0.5_Analysis_Tools_Testing.md | 91 ++++++++++++++++++ ...ask_0.6_Statistics_and_Processing_Tools.md | 92 +++++++++++++++++++ ...sk_0.7_Complete_Test_Suite_and_Coverage.md | 90 ++++++++++++++++++ 7 files changed, 598 insertions(+) create mode 100644 prompts/tasks/Task_0.1_Project_Structure_and_Jest_Setup.md create mode 100644 prompts/tasks/Task_0.2_Transform_Tools_Implementation.md create mode 100644 prompts/tasks/Task_0.3_Transform_Tools_Testing.md create mode 100644 prompts/tasks/Task_0.4_Analysis_Tools_Implementation.md create mode 100644 prompts/tasks/Task_0.5_Analysis_Tools_Testing.md create mode 100644 prompts/tasks/Task_0.6_Statistics_and_Processing_Tools.md create mode 100644 prompts/tasks/Task_0.7_Complete_Test_Suite_and_Coverage.md diff --git a/prompts/tasks/Task_0.1_Project_Structure_and_Jest_Setup.md b/prompts/tasks/Task_0.1_Project_Structure_and_Jest_Setup.md new file mode 100644 index 0000000..b808b37 --- /dev/null +++ b/prompts/tasks/Task_0.1_Project_Structure_and_Jest_Setup.md @@ -0,0 +1,70 @@ +# APM Task Assignment: Project Structure and Jest Setup + +## 1. Agent Role & APM Context + +You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your role as an Implementation Agent is to execute assigned tasks diligently and log your work meticulously to ensure project continuity. You will interact with the Manager Agent (via the User) and contribute to the centralized Memory Bank system for knowledge preservation across the project lifecycle. + +## 2. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 0, Task 0.1 - Project Structure and Jest Setup` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Establish the JavaScript toolbox repository with comprehensive testing framework. + +**Detailed Action Steps:** + +1. **Create project directory structure:** + - Set up `/examples/javascript-bundle/` directory in the project root + - Create the following subdirectories within the bundle: + - `/tools/` - for tool implementations + - `/data/` - for sample data files + - `/tests/` - for test files + - `/schemas/` - for schema definitions + - Initialize `package.json` with Jest testing configuration + - Configure npm scripts: `test`, `test:watch`, `test:coverage` + +2. **Install and configure Jest testing framework:** + - Install Jest with ES6 module support using npm + - Configure Jest for browser-like environment using jsdom + - Set up code coverage reporting with 100% target coverage threshold + - Create shared test utilities in `/tests/helpers.js` for common test functions + +3. **Create sample data and test fixtures:** + - Generate `sample-track.geojson` with GPS track containing timestamps in the `/data/` directory + - Create `sample-features.geojson` with mixed FeatureCollection containing different geometry types + - Add test data for different geometric types: Point, LineString, and Polygon features + - Create reference outputs for validation testing to be used by subsequent test tasks + +**Provide Necessary Context/Assets:** + +The project follows a bundle-based architecture where each tool bundle is self-contained with its dependencies. The JavaScript bundle will serve as the foundation for all subsequent tool implementations in Phase 0. Ensure all configurations support offline operation without external dependencies. + +## 3. Expected Output & Deliverables + +**Define Success:** Successful completion means having a fully configured JavaScript toolbox directory with Jest testing framework ready for tool development and comprehensive test coverage reporting. + +**Specify Deliverables:** +- `/examples/javascript-bundle/` directory with proper subdirectory structure +- Configured `package.json` with Jest dependencies and npm scripts +- Jest configuration supporting ES6 modules and jsdom environment +- Sample GeoJSON data files ready for tool testing +- Basic test helper utilities for shared test functionality +- All configurations validated and ready for tool implementation + +**Format:** Standard npm project structure with Jest configuration following Node.js best practices. + +## 4. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. + +Adhere strictly to the established logging format. Ensure your log includes: +- A reference to the assigned task in the Implementation Plan (Phase 0, Task 0.1) +- A clear description of the actions taken +- Key configuration decisions made for Jest setup +- Directory structure created and rationale +- Sample data files created and their specifications +- Any challenges encountered during setup +- Confirmation of successful execution (tests running, coverage reporting functional) + +## 5. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_0.2_Transform_Tools_Implementation.md b/prompts/tasks/Task_0.2_Transform_Tools_Implementation.md new file mode 100644 index 0000000..9b02c42 --- /dev/null +++ b/prompts/tasks/Task_0.2_Transform_Tools_Implementation.md @@ -0,0 +1,80 @@ +# APM Task Assignment: Transform Tools Implementation + +## 1. Agent Role & APM Context + +You are continuing as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. This task builds directly upon the foundational work completed in Task 0.1. + +## 2. Onboarding / Context from Prior Work + +**Prerequisites:** This task assumes successful completion of Task 0.1 - Project Structure and Jest Setup. The `/examples/javascript-bundle/` directory structure should be established with Jest testing framework configured and sample GeoJSON data files created. + +**Connection to Prior Work:** You will now implement the first set of functional tools using the project structure and sample data established in Task 0.1. The tools you create will be tested using the Jest framework configured in the previous task. + +## 3. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 0, Task 0.2 - Transform Tools Implementation` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Implement geometric transformation tools with IIFE pattern following ADR-013 specification. + +**Detailed Action Steps:** + +1. **Implement Translate Features tool:** + - Create `tools/transform/translate.js` using IIFE (Immediately Invoked Function Expression) pattern + - Function signature must be: `translateFeatures(input, params)` where params = {direction, distance} + - Apply geometric translation to all features in GeoJSON FeatureCollection input + - Handle coordinate system transformations and unit conversions appropriately + - **Critical Guidance:** Use spherical geometry for accurate distance calculations (implement haversine formula or similar) + - Ensure the tool registers itself in `window.ToolVault.tools` namespace as per IIFE pattern + +2. **Implement Flip Horizontal tool:** + - Create `tools/transform/flip.js` with horizontal flip functionality + - Support flipping across longitude axis with proper coordinate handling + - Preserve all feature properties and maintain complete GeoJSON structure + - Handle edge cases like features crossing the antimeridian (180° longitude line) + - Use robust coordinate validation to prevent invalid transformations + +3. **Implement Flip Vertical tool:** + - Extend flip.js with vertical flip capability across latitude axis + - Support axis parameter with values: "longitude" or "latitude" + - Ensure proper coordinate validation and bounds checking (-90 to +90 for latitude) + - Maintain spatial relationships between features during transformation + - Implement error handling for invalid axis parameter values + +**Provide Necessary Context/Assets:** + +- **ADR-013 Reference:** These tools must follow the IIFE pattern specified in ADR-013 for JavaScript tool architecture +- **IIFE Pattern Implementation:** Each tool should wrap its functionality in an IIFE that registers the tool function in the global `window.ToolVault.tools` object +- **Sample Data:** Use the sample GeoJSON files created in Task 0.1 (`sample-track.geojson`, `sample-features.geojson`) for initial testing +- **Coordinate Systems:** Assume WGS84 geographic coordinates (latitude/longitude) for all inputs +- **Distance Calculations:** For translation tool, implement proper spherical distance calculations accounting for Earth's curvature + +## 4. Expected Output & Deliverables + +**Define Success:** Successful completion means having three working transformation tools that can process GeoJSON FeatureCollections and produce valid transformed outputs while preserving data integrity. + +**Specify Deliverables:** +- `tools/transform/translate.js` - Translation tool with spherical geometry calculations +- `tools/transform/flip.js` - Flip tool supporting both horizontal and vertical operations +- Each tool properly registered in `window.ToolVault.tools` namespace +- Tools validated against sample data from Task 0.1 +- All coordinate transformations producing valid GeoJSON outputs +- Proper error handling for invalid inputs and parameters + +**Format:** JavaScript files following IIFE pattern with proper tool registration and parameter validation. + +## 5. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. + +Adhere strictly to the established logging format. Ensure your log includes: +- A reference to the assigned task in the Implementation Plan (Phase 0, Task 0.2) +- A clear description of the transformation algorithms implemented +- Code snippets showing the IIFE pattern implementation and tool registration +- Key decisions made regarding spherical geometry calculations and coordinate handling +- Any challenges encountered with coordinate transformations or edge cases +- Validation results against sample data from Task 0.1 +- Confirmation of successful execution (tools loading and producing valid outputs) + +## 6. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_0.3_Transform_Tools_Testing.md b/prompts/tasks/Task_0.3_Transform_Tools_Testing.md new file mode 100644 index 0000000..095e9e1 --- /dev/null +++ b/prompts/tasks/Task_0.3_Transform_Tools_Testing.md @@ -0,0 +1,86 @@ +# APM Task Assignment: Transform Tools Unit Testing + +## 1. Agent Role & APM Context + +You are activated as a Test Specialist Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your specialized role involves creating comprehensive unit tests with a focus on achieving 100% code coverage and validating tool functionality against known reference data. + +## 2. Onboarding / Context from Prior Work + +**Prerequisites:** This task builds upon the completed work from: +- Task 0.1: Project structure established with Jest testing framework configured +- Task 0.2: Transform tools implemented (`translate.js`, `flip.js`) using IIFE pattern + +**Connection to Prior Work:** You will now create comprehensive unit tests for the transformation tools implemented in Task 0.2, using the Jest framework and test utilities established in Task 0.1. Your tests must validate the spherical geometry calculations, coordinate transformations, and edge case handling implemented by the previous agent. + +## 3. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 0, Task 0.3 - Transform Tools Unit Testing` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Create comprehensive unit tests for all transformation tools with 100% coverage. + +**Detailed Action Steps:** + +1. **Test Translate Features functionality:** + - Create test file `tests/transform/translate.test.js` + - Test translation with known coordinate sets and mathematically verified expected outputs + - Verify distance and direction calculations against reference data using haversine formula validation + - Test edge cases: zero distance, invalid directions (outside 0-360°), empty inputs, malformed GeoJSON + - Validate complete GeoJSON structure preservation including properties, feature types, and metadata + - Test coordinate boundary conditions (poles, antimeridian crossings) + +2. **Test Flip operations:** + - Create test file `tests/transform/flip.test.js` + - Test horizontal flip with coordinates spanning different longitudes, including antimeridian scenarios + - Test vertical flip with various latitude ranges, including polar regions + - Verify axis parameter validation ("longitude", "latitude") and error handling for invalid values + - Test with different geometry types: Point, LineString, Polygon, MultiPolygon + - Validate property preservation and feature collection structure integrity + +3. **Integration testing setup:** + - Create test file `tests/integration/tool-loading.test.js` + - Test tool loading through IIFE pattern using jsdom environment + - Verify `window.ToolVault.tools` namespace availability and proper tool registration + - Test tools with realistic sample data from `/data/` directory (created in Task 0.1) + - Create test suites for browser compatibility using Jest's browser-like environment + - Implement performance benchmarks for transformation operations + +**Provide Necessary Context/Assets:** + +- **Coverage Requirement:** All tests must contribute to achieving 100% code coverage target +- **Reference Data:** Use mathematically verified reference outputs for validation (create these as part of test fixtures) +- **Jest Configuration:** Leverage the Jest configuration established in Task 0.1 with jsdom environment +- **Sample Data:** Utilize sample GeoJSON files from Task 0.1 for realistic testing scenarios +- **Edge Cases:** Pay special attention to coordinate system edge cases (antimeridian, poles, invalid coordinates) + +## 4. Expected Output & Deliverables + +**Define Success:** Successful completion means comprehensive test coverage for all transformation tools with 100% code coverage, validated mathematical accuracy, and robust edge case handling. + +**Specify Deliverables:** +- `tests/transform/translate.test.js` - Complete test suite for translation functionality +- `tests/transform/flip.test.js` - Complete test suite for flip operations +- `tests/integration/tool-loading.test.js` - Integration tests for IIFE tool loading +- Test coverage report showing 100% coverage for transformation tools +- Reference data fixtures for mathematical validation +- Performance benchmark results for transformation operations +- All tests passing with proper error handling validation + +**Format:** Jest test files following established testing patterns with comprehensive assertions and coverage reporting. + +## 5. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. + +Adhere strictly to the established logging format. Ensure your log includes: +- A reference to the assigned task in the Implementation Plan (Phase 0, Task 0.3) +- A clear description of test strategies implemented for each transformation tool +- Code snippets showing critical test cases and assertions +- Mathematical validation approaches used for spherical geometry calculations +- Edge cases identified and tested +- Coverage report results confirming 100% target achievement +- Any challenges encountered with coordinate system testing or Jest configuration +- Confirmation of successful execution (all tests passing, coverage targets met) + +## 6. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_0.4_Analysis_Tools_Implementation.md b/prompts/tasks/Task_0.4_Analysis_Tools_Implementation.md new file mode 100644 index 0000000..f180f88 --- /dev/null +++ b/prompts/tasks/Task_0.4_Analysis_Tools_Implementation.md @@ -0,0 +1,89 @@ +# APM Task Assignment: Analysis Tools Implementation + +## 1. Agent Role & APM Context + +You are continuing as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project, now focusing on temporal analysis capabilities for GPS tracks and time series calculations. + +## 2. Onboarding / Context from Prior Work + +**Prerequisites:** This task builds upon the completed work from: +- Task 0.1: Project structure and Jest framework established +- Task 0.2: Transform tools implemented with IIFE pattern +- Task 0.3: Comprehensive testing framework established for tool validation + +**Connection to Prior Work:** You will now implement temporal analysis tools using the same IIFE pattern and project structure established in previous tasks. These tools will work with GPS track data and extend the functionality beyond geometric transformations to temporal calculations. + +## 3. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 0, Task 0.4 - Analysis Tools Implementation` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Implement temporal analysis tools for GPS tracks and time series calculations. + +**Detailed Action Steps:** + +1. **Implement Calculate Speed Series tool:** + - Create `tools/analysis/speed-series.js` for temporal speed calculations + - Parse timestamps from GeoJSON LineString coordinates (if 3D/4D) or feature properties + - Calculate speed between consecutive points with configurable time_unit parameter (seconds, minutes, hours) + - Return structured JSON time series with timestamp/speed pairs + - **Critical Guidance:** Handle different timestamp formats including ISO 8601 strings and Unix epoch timestamps + - Implement robust timestamp parsing with format detection and validation + - Use haversine formula for accurate distance calculations between GPS points + +2. **Implement Calculate Direction Series tool:** + - Create `tools/analysis/direction-series.js` for bearing/heading calculations + - Calculate bearing (compass direction) between consecutive GPS points using forward azimuth + - Support smoothing parameter with configurable window_size for moving average smoothing + - Return structured JSON time series with timestamp/direction pairs (0-360 degrees) + - Apply smoothing algorithms (moving average) when window_size parameter is provided + - Ensure direction values are properly normalized to 0-360° range + +3. **Handle temporal data edge cases:** + - Validate timestamp presence and format consistency in input data + - Handle irregular time intervals and missing data points gracefully + - Implement comprehensive error handling for invalid temporal sequences + - Support different coordinate systems with proper projection handling + - Add validation for minimum point requirements (need at least 2 points for calculations) + - Handle zero-distance movements and stationary periods appropriately + +**Provide Necessary Context/Assets:** + +- **Temporal Data Formats:** Support common GPS tracking formats with timestamps in coordinates array [lon, lat, elevation, timestamp] or in feature properties +- **Time Unit Conversions:** Implement conversions between seconds, minutes, and hours for speed calculations +- **Smoothing Algorithms:** Use simple moving average for direction smoothing with configurable window sizes +- **IIFE Pattern:** Follow the same pattern established in Task 0.2 for tool registration +- **Sample Data:** Leverage sample GPS track data from Task 0.1 for testing and validation + +## 4. Expected Output & Deliverables + +**Define Success:** Successful completion means having two working temporal analysis tools that can process GPS track data with timestamps and produce accurate time series outputs for speed and direction calculations. + +**Specify Deliverables:** +- `tools/analysis/speed-series.js` - Speed calculation tool with multiple time unit support +- `tools/analysis/direction-series.js` - Direction/bearing tool with optional smoothing +- Both tools properly registered in `window.ToolVault.tools` namespace using IIFE pattern +- Comprehensive timestamp format support (ISO 8601, Unix epoch) +- Robust error handling for temporal data edge cases +- Mathematical accuracy validated against reference calculations +- Time series output in structured JSON format with metadata + +**Format:** JavaScript files following established IIFE pattern with proper temporal data handling and time series output generation. + +## 5. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. + +Adhere strictly to the established logging format. Ensure your log includes: +- A reference to the assigned task in the Implementation Plan (Phase 0, Task 0.4) +- A clear description of temporal analysis algorithms implemented +- Code snippets showing timestamp parsing and time series generation logic +- Key decisions made regarding time format support and edge case handling +- Mathematical approaches used for speed and direction calculations +- Smoothing algorithm implementation details +- Any challenges encountered with temporal data processing +- Validation results against sample GPS track data +- Confirmation of successful execution (tools producing accurate time series outputs) + +## 6. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_0.5_Analysis_Tools_Testing.md b/prompts/tasks/Task_0.5_Analysis_Tools_Testing.md new file mode 100644 index 0000000..702138e --- /dev/null +++ b/prompts/tasks/Task_0.5_Analysis_Tools_Testing.md @@ -0,0 +1,91 @@ +# APM Task Assignment: Analysis Tools Unit Testing + +## 1. Agent Role & APM Context + +You are continuing as a Test Specialist Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your specialized focus is now on validating temporal analysis tools with mathematical precision and comprehensive edge case coverage. + +## 2. Onboarding / Context from Prior Work + +**Prerequisites:** This task builds upon the completed work from: +- Task 0.1-0.3: Project structure, transform tools, and testing framework established +- Task 0.4: Temporal analysis tools implemented (`speed-series.js`, `direction-series.js`) with timestamp parsing and time series generation + +**Connection to Prior Work:** You will create comprehensive unit tests for the temporal analysis tools implemented in Task 0.4, validating mathematical accuracy against known reference data, timestamp processing reliability, and time series output correctness. + +## 3. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 0, Task 0.5 - Analysis Tools Unit Testing` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Verify temporal calculations against known reference data with comprehensive test coverage. + +**Detailed Action Steps:** + +1. **Test Speed Series calculations:** + - Create test file `tests/analysis/speed-series.test.js` + - Create reference GPS track data with mathematically verified speeds for validation + - Test with different time units (seconds, minutes, hours) and verify unit conversion accuracy + - Verify speed calculations using haversine formula against independent reference implementations + - Test edge cases: stationary points (zero speed), high-speed segments, irregular time gaps, missing timestamps + - Validate time series output format and metadata structure + - Test timestamp format handling (ISO 8601, Unix epoch, mixed formats) + +2. **Test Direction Series calculations:** + - Create test file `tests/analysis/direction-series.test.js` + - Validate bearing calculations with known coordinate pairs and expected compass bearings + - Test smoothing algorithms with synthetic data having known smoothing effects + - Verify direction continuity and proper angle normalization (0-360°) + - Test with different window_size values and validate smoothing behavior mathematically + - Test edge cases: 180° bearing crossings, single point inputs, zero-distance segments + - Validate output format consistency with timestamp/direction pairs + +3. **Temporal data validation testing:** + - Create test file `tests/analysis/temporal-validation.test.js` + - Test with various timestamp formats including timezone variations + - Verify comprehensive error handling for missing, invalid, or inconsistent timestamps + - Test performance with large datasets (1000+ GPS points with timestamps) + - Validate memory usage patterns and execution time constraints + - Test with malformed temporal data and ensure graceful error handling + - Verify handling of out-of-order timestamps and duplicate time entries + +**Provide Necessary Context/Assets:** + +- **Mathematical Validation:** Create reference datasets with independently calculated speed and bearing values +- **Performance Requirements:** Test with realistic GPS track sizes typical for mobile applications +- **Timestamp Precision:** Test various timestamp precisions (second, millisecond, microsecond) +- **Coverage Target:** Maintain 100% code coverage goal established in previous testing tasks +- **Error Scenarios:** Test comprehensive error conditions for robust temporal data handling + +## 4. Expected Output & Deliverables + +**Define Success:** Successful completion means comprehensive test coverage for temporal analysis tools with mathematically validated accuracy, robust timestamp handling, and performance benchmarks meeting requirements. + +**Specify Deliverables:** +- `tests/analysis/speed-series.test.js` - Complete test suite for speed calculations +- `tests/analysis/direction-series.test.js` - Complete test suite for direction/bearing calculations +- `tests/analysis/temporal-validation.test.js` - Temporal data validation and error handling tests +- Mathematical reference data fixtures for validation +- Performance benchmark results for large dataset processing +- Test coverage report maintaining 100% coverage for analysis tools +- Error handling validation covering all temporal edge cases +- All tests passing with mathematical accuracy verified + +**Format:** Jest test files with comprehensive mathematical validation, performance benchmarks, and detailed assertions for temporal data processing accuracy. + +## 5. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. + +Adhere strictly to the established logging format. Ensure your log includes: +- A reference to the assigned task in the Implementation Plan (Phase 0, Task 0.5) +- A clear description of temporal analysis testing strategies implemented +- Mathematical validation approaches used for speed and bearing calculations +- Code snippets showing critical test cases for timestamp handling and edge cases +- Performance benchmarking methodology and results +- Reference data creation process and validation accuracy +- Any challenges encountered with temporal data testing or mathematical precision +- Coverage report results confirming 100% target maintenance +- Confirmation of successful execution (all temporal tests passing with verified accuracy) + +## 6. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_0.6_Statistics_and_Processing_Tools.md b/prompts/tasks/Task_0.6_Statistics_and_Processing_Tools.md new file mode 100644 index 0000000..e4c6be8 --- /dev/null +++ b/prompts/tasks/Task_0.6_Statistics_and_Processing_Tools.md @@ -0,0 +1,92 @@ +# APM Task Assignment: Statistics and Processing Tools Implementation + +## 1. Agent Role & APM Context + +You are continuing as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project, now focusing on completing the comprehensive tool suite with statistical analysis and data processing capabilities. + +## 2. Onboarding / Context from Prior Work + +**Prerequisites:** This task builds upon the completed work from: +- Task 0.1-0.3: Project structure and transform tools with comprehensive testing +- Task 0.4-0.5: Temporal analysis tools implemented and tested +- Established IIFE pattern and tool registration system from previous tasks + +**Connection to Prior Work:** You will complete the JavaScript tool suite by implementing the remaining categories of tools: Statistics, Processing, and I/O. These tools will round out the functionality and provide comprehensive data analysis capabilities building on the foundation established in previous tasks. + +## 3. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 0, Task 0.6 - Statistics and Processing Tools` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Complete the tool suite with statistical analysis and data processing capabilities. + +**Detailed Action Steps:** + +1. **Implement Statistical tools:** + - Create `tools/statistics/average-speed.js` for speed averaging with configurable time_unit support (seconds, minutes, hours) + - Create `tools/statistics/speed-histogram.js` with configurable bins parameter and interval_minutes for time-based binning + - Calculate comprehensive statistical measures: mean, median, standard deviation, min, max values + - Return structured JSON outputs with statistical metadata including sample size, confidence intervals + - Implement robust handling of outliers and edge cases in statistical calculations + - Support weighted averages for non-uniform time intervals + +2. **Implement Processing tools:** + - Create `tools/processing/smooth-polyline.js` supporting multiple smoothing algorithms + - Support algorithm parameter with options: "moving_average" and "gaussian" smoothing methods + - Implement configurable window_size parameter for both smoothing approaches + - Preserve LineString structure while smoothing coordinate sequences accurately + - Maintain topology integrity and avoid creating self-intersections during smoothing + - Handle edge effects at polyline endpoints appropriately + +3. **Implement I/O tools:** + - Create `tools/io/import-rep.js` for REP file format parsing (GPS track format) + - Create `tools/io/export-rep.js` for REP format export with proper formatting + - Create `tools/io/export-csv.js` for CSV export with multiple output options + - Handle different encoding formats (UTF-8, ASCII) and coordinate precision settings + - Support coordinate_format parameter for CSV: "separate" (lat,lon columns) or "wkt" (Well-Known Text) + - Implement comprehensive error handling for file format validation and parsing + +**Provide Necessary Context/Assets:** + +- **REP Format Specification:** REP is a common GPS tracking format - research and implement proper parsing/export +- **Statistical Accuracy:** Use mathematically sound algorithms for statistical calculations, handle edge cases like empty datasets +- **Smoothing Algorithms:** Implement proper gaussian and moving average filters with appropriate kernel sizes +- **CSV Standards:** Follow RFC 4180 CSV specification with proper escaping and delimiter handling +- **IIFE Pattern:** Maintain consistency with tool registration pattern from previous tasks +- **Error Handling:** Provide comprehensive validation for all input parameters and data formats + +## 4. Expected Output & Deliverables + +**Define Success:** Successful completion means having a complete JavaScript tool suite with statistical analysis, data processing, and I/O capabilities that integrate seamlessly with previously implemented tools. + +**Specify Deliverables:** +- `tools/statistics/average-speed.js` - Statistical averaging tool with time unit support +- `tools/statistics/speed-histogram.js` - Histogram generation tool with configurable binning +- `tools/processing/smooth-polyline.js` - Multi-algorithm polyline smoothing tool +- `tools/io/import-rep.js` - REP format import functionality +- `tools/io/export-rep.js` - REP format export functionality +- `tools/io/export-csv.js` - Flexible CSV export with coordinate format options +- All tools properly registered using IIFE pattern in `window.ToolVault.tools` +- Comprehensive parameter validation and error handling +- Mathematical accuracy in statistical calculations and smoothing algorithms + +**Format:** JavaScript files following established IIFE pattern with comprehensive input validation, statistical accuracy, and proper file format handling. + +## 5. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. + +Adhere strictly to the established logging format. Ensure your log includes: +- A reference to the assigned task in the Implementation Plan (Phase 0, Task 0.6) +- A clear description of statistical algorithms and smoothing methods implemented +- Code snippets showing key functionality for statistics, processing, and I/O operations +- REP file format parsing approach and validation strategy +- Statistical calculation methodologies and accuracy considerations +- Smoothing algorithm implementation details and topology preservation methods +- File format handling and encoding support decisions +- Any challenges encountered with mathematical precision or file format specifications +- Integration testing results with previously implemented tools +- Confirmation of successful execution (all tools working with proper I/O functionality) + +## 6. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_0.7_Complete_Test_Suite_and_Coverage.md b/prompts/tasks/Task_0.7_Complete_Test_Suite_and_Coverage.md new file mode 100644 index 0000000..d2a8557 --- /dev/null +++ b/prompts/tasks/Task_0.7_Complete_Test_Suite_and_Coverage.md @@ -0,0 +1,90 @@ +# APM Task Assignment: Complete Test Suite and Coverage + +## 1. Agent Role & APM Context + +You are concluding Phase 0 as a Test Specialist Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your role is to ensure comprehensive test coverage, create the final bundle documentation, and validate the complete JavaScript toolbox for Phase 1 integration. + +## 2. Onboarding / Context from Prior Work + +**Prerequisites:** This task represents the culmination of Phase 0 work: +- Task 0.1-0.2: Project structure and transform tools implemented +- Task 0.3: Transform tools testing completed +- Task 0.4-0.5: Analysis tools implemented and tested +- Task 0.6: Statistics, processing, and I/O tools completed + +**Connection to Prior Work:** You will now complete the final testing phase, ensure 100% coverage across all implemented tools, create the comprehensive `index.json` metadata file that will drive the Phase 1 frontend, and validate the entire JavaScript toolbox is ready for integration. + +## 3. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 0, Task 0.7 - Complete Test Suite and Coverage` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Ensure 100% test coverage across all tools with comprehensive validation and complete bundle documentation. + +**Detailed Action Steps:** + +1. **Test Statistical tools:** + - Create `tests/statistics/average-speed.test.js` and `tests/statistics/speed-histogram.test.js` + - Validate statistical calculations against reference implementations (independent mathematical verification) + - Test histogram generation with different bin sizes and time intervals, verify bin distributions + - Verify edge cases: empty datasets, single data points, extreme outliers, negative values + - Test numerical precision and proper rounding behavior for statistical measures + - Validate output format consistency and metadata completeness + +2. **Test Processing and I/O tools:** + - Create test files: `tests/processing/smooth-polyline.test.js`, `tests/io/import-rep.test.js`, `tests/io/export-rep.test.js`, `tests/io/export-csv.test.js` + - Validate smoothing algorithms with known input/output pairs (create synthetic test data with expected smoothed results) + - Test REP file format parsing with sample files, validate format compliance and error handling + - Verify CSV/REP export format compliance with standards (RFC 4180 for CSV) + - Test file handling with different encodings (UTF-8, ASCII) and coordinate precision settings + - Validate coordinate format options and proper escaping in CSV exports + +3. **Complete coverage and documentation:** + - Generate and analyze comprehensive code coverage report to achieve 100% coverage target + - Create complete `index.json` file with runtime field ("javascript") and comprehensive metadata for all tools + - Document complete tool specifications including parameter schemas, input/output formats + - Generate performance benchmarks for all tool categories and document execution times + - Create final testing approach documentation summarizing testing strategies and coverage achievements + - Validate entire toolbox integration and cross-tool compatibility + +**Provide Necessary Context/Assets:** + +- **index.json Requirements:** This file will be consumed by Phase 1 frontend for dynamic UI generation - ensure completeness +- **Runtime Field:** Must specify "javascript" runtime to enable proper tool loading in Phase 1 +- **Metadata Completeness:** Include all parameter schemas, validation rules, output formats for frontend integration +- **Performance Benchmarks:** Document execution times for Phase 1 performance optimization planning +- **Coverage Standards:** Maintain 100% coverage requirement established in previous testing tasks + +## 4. Expected Output & Deliverables + +**Define Success:** Successful completion means having a fully tested, documented, and validated JavaScript toolbox with 100% test coverage, complete metadata, and readiness for Phase 1 frontend integration. + +**Specify Deliverables:** +- Complete test suites for all Statistics, Processing, and I/O tools +- 100% code coverage report across entire JavaScript toolbox +- Comprehensive `index.json` with runtime field and complete tool metadata +- Performance benchmark documentation for all tool categories +- Testing approach documentation and coverage analysis +- Cross-tool compatibility validation results +- Complete tool specification documentation ready for Phase 1 integration +- All tests passing with comprehensive edge case coverage + +**Format:** Jest test files with complete coverage reporting, comprehensive `index.json` following metadata standards, and documentation suitable for handover to Phase 1 development. + +## 5. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. + +Adhere strictly to the established logging format. Ensure your log includes: +- A reference to the assigned task in the Implementation Plan (Phase 0, Task 0.7) +- Comprehensive summary of Phase 0 completion and testing achievements +- Code coverage analysis and final coverage report results +- `index.json` creation methodology and metadata completeness verification +- Performance benchmarking results and analysis for all tool categories +- Cross-tool integration testing results and compatibility validation +- Any challenges encountered in achieving 100% coverage or metadata completeness +- Phase 1 handover readiness assessment and integration recommendations +- Confirmation of successful Phase 0 completion (all tools tested, documented, and validated) + +## 6. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file From 2a56e753a2a495f464622c0ada6549869e7826e7 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 14:59:00 +0100 Subject: [PATCH 07/18] docs: update project structure and add detailed implementation docs --- CLAUDE.md | 170 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 71 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8df5e5c..8c4e463 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,109 +8,137 @@ ToolVault is a portable, self-contained service that delivers curated collection ## Project Structure -### Current Structure +### Current Implementation ``` -/docs # Documentation including software requirements -/docs/ADRs # Architecture Decision Records -/prompts # APM (Agentic Project Management) framework guides -/tasks # Task tracking directory +/docs # Documentation including software requirements +/docs/ADRs # Architecture Decision Records +/prompts # APM (Agentic Project Management) framework guides +/prompts/tasks # Task Assignment Prompts for Phase 0 implementation +/examples/javascript-bundle # Mock JavaScript tool implementation (ACTIVE) +├── tools/ # JavaScript tool implementations using IIFE pattern +├── data/ # Sample GeoJSON data files +├── tests/ # Jest test files +├── index.json # Tool metadata for UI generation +└── package.json # Jest testing configuration ``` ### Planned Structure ``` -/client # SPA frontend (to be created) -/server # Flask/FastAPI backend (to be created) -/indexer # Bundle creation scripts (to be created) -/examples # Example tool bundles (to be created) +/client # React/TypeScript SPA frontend (Phase 1) +/server # Flask/FastAPI backend (Phase 3) +/indexer # Bundle creation scripts (Phase 2) ``` -## Key Technologies +## Current Development Phase -### Frontend (Planned) -- Single Page Application (SPA) -- LeafletJS for spatial data visualization -- Metadata-driven UI from `index.json` - -### Backend (Planned) -- Flask or FastAPI for REST API -- Embedded Jupyter server for notebooks -- Python-based tool execution +**Phase 0: JavaScript Mock Tools** (ACTIVE) +- Complete JavaScript tool bundle with 12 tools across 5 categories +- Tools use IIFE pattern and register in `window.ToolVault.tools` namespace +- Full Jest test suite with 100% coverage requirement +- Comprehensive `index.json` metadata for frontend integration ## Architecture Decisions -### Key Constraints -- **Offline capability**: All dependencies must be packaged in bundles -- **50 MB cap** for initial bundles -- **No authentication** in early phases -- **LeafletJS** preferred for spatial rendering - -## APM Framework - -This project uses an Agentic Project Management (APM) framework for AI-assisted development. Key APM assets include: -- Implementation plans -- Memory banks for tracking decisions -- Task assignment prompts -- Handover protocols for context transfer - -When working with APM artifacts, refer to guides in `/prompts/01_Manager_Agent_Core_Guides/`. +### Key Architectural Patterns +- **Metadata-driven UI**: All interfaces generated from `index.json` (ADR-001) +- **Bundle-based distribution**: Self-contained tool packages (ADR-003) +- **IIFE tool pattern**: JavaScript tools use Immediately Invoked Function Expression pattern (ADR-013) +- **Offline-first**: All dependencies packaged in bundles +- **LeafletJS for spatial**: Preferred for GeoJSON visualization (ADR-005) + +### Tool Implementation Pattern (JavaScript Bundle) +```javascript +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.toolName = function(input, params) { + // Tool implementation + return result; + }; +})(); +``` ## Development Commands -Since no implementation exists yet, here are the anticipated commands for each component: - -### Frontend (Future) +### JavaScript Bundle Testing ```bash +cd examples/javascript-bundle + # Install dependencies npm install -# Development server -npm run dev +# Run all tests +npm test -# Build for production -npm run build +# Run tests in watch mode +npm run test:watch -# Run tests -npm test +# Run specific test file +npx jest tests/transform/translate.test.js -# Lint code -npm run lint +# Generate coverage report +npx jest --coverage ``` -### Backend (Future) -```bash -# Create virtual environment -python -m venv venv -source venv/bin/activate # On Windows: venv\Scripts\activate +### APM Framework Usage +The project uses an Agentic Project Management (APM) framework with specialized agents: +- **Agent_JS_Dev**: JavaScript tool implementation +- **Agent_Test_Specialist**: Comprehensive testing with 100% coverage +- **Agent_Frontend_Lead**: React/TypeScript frontend (Phase 1) -# Install dependencies -pip install -r requirements.txt +Task Assignment Prompts (TAPs) are in `/prompts/tasks/` and follow the pattern `Task_X.Y_Description.md`. -# Run development server -python app.py # or: uvicorn main:app --reload +## Tool Categories and Metadata -# Run tests -pytest +The JavaScript bundle implements 12 tools across 5 categories: -# Format code -black . +1. **Transform**: `translate-features`, `flip-horizontal`, `flip-vertical` +2. **Analysis**: `calculate-speed-series`, `calculate-direction-series` +3. **Statistics**: `average-speed`, `speed-histogram` +4. **Processing**: `smooth-polyline` +5. **I/O**: `import-rep`, `export-rep`, `export-csv` -# Lint code -ruff check . -``` +All tools are defined in `examples/javascript-bundle/index.json` with complete metadata including: +- Input/output specifications +- Parameter schemas with defaults and validation +- Runtime type ("javascript") +- Category and tag classifications -### Indexer (Future) -```bash -# Generate bundle from repository -python indexer.py --repo /path/to/tools --output bundle.zip +## Testing Strategy -# Validate index.json -python validate_index.py index.json +### Coverage Requirements +- **100% code coverage** across all tool implementations +- Mathematical validation against reference data +- Edge case handling for coordinate systems, temporal data +- Integration testing for IIFE pattern tool loading +- Performance benchmarks for large datasets + +### Test File Organization +``` +/tests +├── transform/ # Geometric transformation tests +├── analysis/ # Temporal analysis tests +├── statistics/ # Statistical calculation tests +├── processing/ # Data processing tests +├── io/ # Import/export tests +└── integration/ # Cross-tool integration tests ``` +## Key Files and Their Purposes + +- `Implementation_Plan.md`: 12-week development roadmap across 4 phases +- `examples/javascript-bundle/index.json`: Complete tool metadata for Phase 1 UI generation +- `prompts/tasks/Task_0.X_*.md`: Detailed task assignments for Phase 0 completion +- `Memory_Bank.md`: Centralized logging for all agent work and decisions (if exists) +- `docs/ADRs/ADR-013-mock-javascript-toolset.md`: JavaScript tool implementation specification + ## Important Notes - The project uses Python for backend development with Flask/FastAPI preference -- Frontend should be framework-agnostic initially but production-ready -- All code should support offline operation without internet connectivity -- Spatial outputs should be rendered using LeafletJS -- The VS Code extension will integrate with the Debrief themed distribution \ No newline at end of file +- Frontend will be React/TypeScript SPA with metadata-driven UI generation +- All code supports offline operation without internet connectivity +- Spatial outputs are rendered using LeafletJS +- In the future ToolVault will be consumed by Debrief, delivered as VS Code extensions +- JavaScript tools in Phase 0 use spherical geometry for accurate GPS calculations +- All temporal analysis tools support multiple timestamp formats (ISO 8601, Unix epoch) \ No newline at end of file From 59206ce5ab1966a7425592f964b4902f1a54741d Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 15:29:59 +0100 Subject: [PATCH 08/18] chore: remove javascript-bundle example directory and its contents --- .claude/settings.local.json | 4 +- examples/javascript-bundle/.gitignore | 5 - .../data/sample-track.geojson | 36 -- examples/javascript-bundle/index.json | 399 ------------------ examples/javascript-bundle/package.json | 23 - examples/javascript-bundle/tests/helpers.js | 55 --- .../javascript-bundle/tests/tools.test.js | 251 ----------- .../tools/analysis/direction-series.js | 77 ---- .../tools/analysis/speed-series.js | 70 --- .../javascript-bundle/tools/io/export-csv.js | 141 ------- .../javascript-bundle/tools/io/export-rep.js | 87 ---- .../javascript-bundle/tools/io/import-rep.js | 81 ---- .../tools/processing/smooth-polyline.js | 91 ---- .../tools/statistics/average-speed.js | 68 --- .../tools/statistics/speed-histogram.js | 82 ---- .../tools/transform/flip-horizontal.js | 87 ---- .../tools/transform/flip-vertical.js | 87 ---- .../tools/transform/translate.js | 56 --- 18 files changed, 3 insertions(+), 1697 deletions(-) delete mode 100644 examples/javascript-bundle/.gitignore delete mode 100644 examples/javascript-bundle/data/sample-track.geojson delete mode 100644 examples/javascript-bundle/index.json delete mode 100644 examples/javascript-bundle/package.json delete mode 100644 examples/javascript-bundle/tests/helpers.js delete mode 100644 examples/javascript-bundle/tests/tools.test.js delete mode 100644 examples/javascript-bundle/tools/analysis/direction-series.js delete mode 100644 examples/javascript-bundle/tools/analysis/speed-series.js delete mode 100644 examples/javascript-bundle/tools/io/export-csv.js delete mode 100644 examples/javascript-bundle/tools/io/export-rep.js delete mode 100644 examples/javascript-bundle/tools/io/import-rep.js delete mode 100644 examples/javascript-bundle/tools/processing/smooth-polyline.js delete mode 100644 examples/javascript-bundle/tools/statistics/average-speed.js delete mode 100644 examples/javascript-bundle/tools/statistics/speed-histogram.js delete mode 100644 examples/javascript-bundle/tools/transform/flip-horizontal.js delete mode 100644 examples/javascript-bundle/tools/transform/flip-vertical.js delete mode 100644 examples/javascript-bundle/tools/transform/translate.js diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 6c1ca33..af176b4 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -101,7 +101,9 @@ "Bash(open http://localhost:5176)", "Bash(VITE_PORT=5175 pnpm dev)", "Bash(open http://localhost:5175/tools/change-color-to-red)", - "WebFetch(domain:modelcontextprotocol.io)" + "WebFetch(domain:modelcontextprotocol.io)", + "Bash(npm run coverage:*)", + "WebFetch(domain:debrief.github.io)" ] } } \ No newline at end of file diff --git a/examples/javascript-bundle/.gitignore b/examples/javascript-bundle/.gitignore deleted file mode 100644 index ae2e759..0000000 --- a/examples/javascript-bundle/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules/ -package-lock.json -coverage/ -.DS_Store -*.log \ No newline at end of file diff --git a/examples/javascript-bundle/data/sample-track.geojson b/examples/javascript-bundle/data/sample-track.geojson deleted file mode 100644 index 7035059..0000000 --- a/examples/javascript-bundle/data/sample-track.geojson +++ /dev/null @@ -1,36 +0,0 @@ -{ - "type": "Feature", - "properties": { - "name": "Sample GPS Track", - "description": "A sample GPS track for testing speed and direction calculations" - }, - "geometry": { - "type": "LineString", - "coordinates": [ - [-0.1276, 51.5074], - [-0.1278, 51.5076], - [-0.1280, 51.5078], - [-0.1283, 51.5080], - [-0.1286, 51.5082], - [-0.1289, 51.5084], - [-0.1292, 51.5086], - [-0.1295, 51.5088], - [-0.1298, 51.5090], - [-0.1301, 51.5092] - ], - "properties": { - "timestamps": [ - "2024-01-18T10:00:00Z", - "2024-01-18T10:00:05Z", - "2024-01-18T10:00:10Z", - "2024-01-18T10:00:15Z", - "2024-01-18T10:00:20Z", - "2024-01-18T10:00:25Z", - "2024-01-18T10:00:30Z", - "2024-01-18T10:00:35Z", - "2024-01-18T10:00:40Z", - "2024-01-18T10:00:45Z" - ] - } - } -} \ No newline at end of file diff --git a/examples/javascript-bundle/index.json b/examples/javascript-bundle/index.json deleted file mode 100644 index b157729..0000000 --- a/examples/javascript-bundle/index.json +++ /dev/null @@ -1,399 +0,0 @@ -{ - "bundle": { - "name": "JavaScript GeoJSON Tools", - "version": "1.0.0", - "description": "Mock JavaScript tool bundle for ToolVault UI testing with GeoJSON processing capabilities", - "created": "2024-01-18", - "categories": ["transform", "analysis", "statistics", "processing", "io"] - }, - "tools": [ - { - "id": "translate-features", - "name": "Translate Features", - "description": "Move GeoJSON features by a specified direction and distance", - "category": "transform", - "runtime": "javascript", - "script": "tools/transform/translate.js", - "function": "translateFeatures", - "inputs": [ - { - "name": "features", - "type": "GeoJSON", - "description": "GeoJSON FeatureCollection to translate" - } - ], - "parameters": [ - { - "name": "direction", - "type": "number", - "description": "Direction in degrees (0-360)", - "default": 0 - }, - { - "name": "distance", - "type": "number", - "description": "Distance in meters", - "default": 100 - } - ], - "outputs": [ - { - "name": "result", - "type": "GeoJSON", - "description": "Translated GeoJSON FeatureCollection" - } - ], - "tags": ["geometry", "transform", "spatial"] - }, - { - "id": "flip-horizontal", - "name": "Flip Horizontal", - "description": "Mirror GeoJSON features horizontally across a reference axis", - "category": "transform", - "runtime": "javascript", - "script": "tools/transform/flip-horizontal.js", - "function": "flipHorizontal", - "inputs": [ - { - "name": "features", - "type": "GeoJSON", - "description": "GeoJSON FeatureCollection to flip" - } - ], - "parameters": [ - { - "name": "axis", - "type": "string", - "description": "Reference axis (longitude or latitude)", - "default": "longitude", - "options": ["longitude", "latitude"] - } - ], - "outputs": [ - { - "name": "result", - "type": "GeoJSON", - "description": "Flipped GeoJSON FeatureCollection" - } - ], - "tags": ["geometry", "transform", "mirror"] - }, - { - "id": "flip-vertical", - "name": "Flip Vertical", - "description": "Mirror GeoJSON features vertically across a reference axis", - "category": "transform", - "runtime": "javascript", - "script": "tools/transform/flip-vertical.js", - "function": "flipVertical", - "inputs": [ - { - "name": "features", - "type": "GeoJSON", - "description": "GeoJSON FeatureCollection to flip" - } - ], - "parameters": [ - { - "name": "axis", - "type": "string", - "description": "Reference axis (longitude or latitude)", - "default": "latitude", - "options": ["longitude", "latitude"] - } - ], - "outputs": [ - { - "name": "result", - "type": "GeoJSON", - "description": "Flipped GeoJSON FeatureCollection" - } - ], - "tags": ["geometry", "transform", "mirror"] - }, - { - "id": "calculate-speed-series", - "name": "Calculate Speed Series", - "description": "Generate time series of speeds from a GPS track", - "category": "analysis", - "runtime": "javascript", - "script": "tools/analysis/speed-series.js", - "function": "calculateSpeedSeries", - "inputs": [ - { - "name": "track", - "type": "GeoJSON", - "description": "GeoJSON LineString with timestamp properties" - } - ], - "parameters": [ - { - "name": "time_unit", - "type": "string", - "description": "Time unit for speed calculation", - "default": "seconds", - "options": ["seconds", "minutes", "hours"] - } - ], - "outputs": [ - { - "name": "series", - "type": "JSON", - "description": "Array of {time, speed} objects" - } - ], - "tags": ["temporal", "analysis", "gps", "speed"] - }, - { - "id": "calculate-direction-series", - "name": "Calculate Direction Series", - "description": "Generate time series of travel directions from a GPS track", - "category": "analysis", - "runtime": "javascript", - "script": "tools/analysis/direction-series.js", - "function": "calculateDirectionSeries", - "inputs": [ - { - "name": "track", - "type": "GeoJSON", - "description": "GeoJSON LineString with timestamp properties" - } - ], - "parameters": [ - { - "name": "smoothing", - "type": "boolean", - "description": "Apply smoothing to directions", - "default": false - }, - { - "name": "window_size", - "type": "number", - "description": "Smoothing window size", - "default": 3 - } - ], - "outputs": [ - { - "name": "series", - "type": "JSON", - "description": "Array of {time, direction} objects" - } - ], - "tags": ["temporal", "analysis", "gps", "bearing"] - }, - { - "id": "average-speed", - "name": "Average Speed", - "description": "Calculate average speed from a GPS track", - "category": "statistics", - "runtime": "javascript", - "script": "tools/statistics/average-speed.js", - "function": "calculateAverageSpeed", - "inputs": [ - { - "name": "track", - "type": "GeoJSON", - "description": "GeoJSON LineString with timestamp properties" - } - ], - "parameters": [ - { - "name": "time_unit", - "type": "string", - "description": "Time unit for speed calculation", - "default": "seconds", - "options": ["seconds", "minutes", "hours"] - } - ], - "outputs": [ - { - "name": "speed", - "type": "number", - "description": "Average speed value" - } - ], - "tags": ["statistics", "gps", "speed"] - }, - { - "id": "speed-histogram", - "name": "Speed Histogram", - "description": "Create histogram of speeds with configurable intervals", - "category": "statistics", - "runtime": "javascript", - "script": "tools/statistics/speed-histogram.js", - "function": "createSpeedHistogram", - "inputs": [ - { - "name": "track", - "type": "GeoJSON", - "description": "GeoJSON LineString with timestamp properties" - } - ], - "parameters": [ - { - "name": "interval_minutes", - "type": "number", - "description": "Time interval in minutes", - "default": 1 - }, - { - "name": "bins", - "type": "number", - "description": "Number of histogram bins", - "default": 20 - } - ], - "outputs": [ - { - "name": "histogram", - "type": "JSON", - "description": "Histogram data structure" - } - ], - "tags": ["statistics", "visualization", "gps", "speed"] - }, - { - "id": "smooth-polyline", - "name": "Smooth Polyline", - "description": "Apply smoothing algorithms to LineString geometries", - "category": "processing", - "runtime": "javascript", - "script": "tools/processing/smooth-polyline.js", - "function": "smoothPolyline", - "inputs": [ - { - "name": "linestring", - "type": "GeoJSON", - "description": "GeoJSON LineString to smooth" - } - ], - "parameters": [ - { - "name": "algorithm", - "type": "string", - "description": "Smoothing algorithm", - "default": "moving_average", - "options": ["moving_average", "gaussian"] - }, - { - "name": "window_size", - "type": "number", - "description": "Smoothing window size", - "default": 3 - } - ], - "outputs": [ - { - "name": "result", - "type": "GeoJSON", - "description": "Smoothed GeoJSON LineString" - } - ], - "tags": ["geometry", "processing", "smoothing"] - }, - { - "id": "import-rep", - "name": "Import REP File", - "description": "Parse REP format file to GeoJSON", - "category": "io", - "runtime": "javascript", - "script": "tools/io/import-rep.js", - "function": "importREP", - "inputs": [ - { - "name": "content", - "type": "text", - "description": "REP format text content" - } - ], - "parameters": [ - { - "name": "encoding", - "type": "string", - "description": "Text encoding", - "default": "utf-8" - } - ], - "outputs": [ - { - "name": "features", - "type": "GeoJSON", - "description": "Parsed GeoJSON FeatureCollection" - } - ], - "tags": ["import", "conversion", "rep"] - }, - { - "id": "export-rep", - "name": "Export to REP", - "description": "Convert GeoJSON to REP format", - "category": "io", - "runtime": "javascript", - "script": "tools/io/export-rep.js", - "function": "exportREP", - "inputs": [ - { - "name": "features", - "type": "GeoJSON", - "description": "GeoJSON FeatureCollection to export" - } - ], - "parameters": [ - { - "name": "precision", - "type": "number", - "description": "Coordinate precision", - "default": 6 - } - ], - "outputs": [ - { - "name": "content", - "type": "text", - "description": "REP format text" - } - ], - "tags": ["export", "conversion", "rep"] - }, - { - "id": "export-csv", - "name": "Export to CSV", - "description": "Convert GeoJSON to CSV format", - "category": "io", - "runtime": "javascript", - "script": "tools/io/export-csv.js", - "function": "exportCSV", - "inputs": [ - { - "name": "features", - "type": "GeoJSON", - "description": "GeoJSON FeatureCollection to export" - } - ], - "parameters": [ - { - "name": "include_properties", - "type": "boolean", - "description": "Include feature properties in CSV", - "default": true - }, - { - "name": "coordinate_format", - "type": "string", - "description": "Format for coordinates", - "default": "separate", - "options": ["separate", "wkt"] - } - ], - "outputs": [ - { - "name": "content", - "type": "text", - "description": "CSV format text" - } - ], - "tags": ["export", "conversion", "csv"] - } - ] -} \ No newline at end of file diff --git a/examples/javascript-bundle/package.json b/examples/javascript-bundle/package.json deleted file mode 100644 index 7e31029..0000000 --- a/examples/javascript-bundle/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "toolvault-javascript-bundle", - "version": "1.0.0", - "description": "Mock JavaScript tool bundle for ToolVault UI testing", - "scripts": { - "test": "jest", - "test:watch": "jest --watch" - }, - "devDependencies": { - "@types/jest": "^29.5.0", - "jest": "^29.7.0", - "jest-environment-jsdom": "^29.7.0" - }, - "jest": { - "testEnvironment": "jsdom", - "testMatch": [ - "**/tests/**/*.test.js" - ], - "collectCoverageFrom": [ - "tools/**/*.js" - ] - } -} diff --git a/examples/javascript-bundle/tests/helpers.js b/examples/javascript-bundle/tests/helpers.js deleted file mode 100644 index 37c2af8..0000000 --- a/examples/javascript-bundle/tests/helpers.js +++ /dev/null @@ -1,55 +0,0 @@ -// Test helpers and fixtures - -const samplePoint = { - type: 'Feature', - properties: { name: 'Test Point' }, - geometry: { - type: 'Point', - coordinates: [-0.1276, 51.5074] - } -}; - -const sampleLineString = { - type: 'Feature', - properties: { name: 'Test Line' }, - geometry: { - type: 'LineString', - coordinates: [ - [-0.1276, 51.5074], - [-0.1278, 51.5076], - [-0.1280, 51.5078] - ] - } -}; - -const sampleTrackWithTimestamps = { - type: 'Feature', - properties: { name: 'GPS Track' }, - geometry: { - type: 'LineString', - coordinates: [ - [-0.1276, 51.5074], - [-0.1278, 51.5076], - [-0.1280, 51.5078] - ], - properties: { - timestamps: [ - '2024-01-18T10:00:00Z', - '2024-01-18T10:00:05Z', - '2024-01-18T10:00:10Z' - ] - } - } -}; - -const sampleFeatureCollection = { - type: 'FeatureCollection', - features: [samplePoint, sampleLineString] -}; - -module.exports = { - samplePoint, - sampleLineString, - sampleTrackWithTimestamps, - sampleFeatureCollection -}; \ No newline at end of file diff --git a/examples/javascript-bundle/tests/tools.test.js b/examples/javascript-bundle/tests/tools.test.js deleted file mode 100644 index 4f5cd7e..0000000 --- a/examples/javascript-bundle/tests/tools.test.js +++ /dev/null @@ -1,251 +0,0 @@ -// Load all tool files -require('../tools/transform/translate.js'); -require('../tools/transform/flip-horizontal.js'); -require('../tools/transform/flip-vertical.js'); -require('../tools/analysis/speed-series.js'); -require('../tools/analysis/direction-series.js'); -require('../tools/statistics/average-speed.js'); -require('../tools/statistics/speed-histogram.js'); -require('../tools/processing/smooth-polyline.js'); -require('../tools/io/import-rep.js'); -require('../tools/io/export-rep.js'); -require('../tools/io/export-csv.js'); - -const { - samplePoint, - sampleLineString, - sampleTrackWithTimestamps, - sampleFeatureCollection -} = require('./helpers'); - -// Initialize window object for browser environment simulation -global.window = global.window || {}; - -describe('Transform Tools', () => { - describe('translateFeatures', () => { - test('should translate features by specified distance and direction', () => { - const input = JSON.parse(JSON.stringify(samplePoint)); - const params = { direction: 0, distance: 100 }; - const result = window.ToolVault.tools.translateFeatures(input, params); - - expect(result.type).toBe('Feature'); - expect(result.geometry.type).toBe('Point'); - // Check that coordinates have changed - expect(result.geometry.coordinates[1]).toBeGreaterThan(input.geometry.coordinates[1]); - }); - - test('should handle FeatureCollection', () => { - const input = JSON.parse(JSON.stringify(sampleFeatureCollection)); - const params = { direction: 90, distance: 100 }; - const result = window.ToolVault.tools.translateFeatures(input, params); - - expect(result.type).toBe('FeatureCollection'); - expect(result.features).toHaveLength(2); - }); - }); - - describe('flipHorizontal', () => { - test('should flip features horizontally', () => { - const input = JSON.parse(JSON.stringify(sampleLineString)); - const params = { axis: 'longitude' }; - const result = window.ToolVault.tools.flipHorizontal(input, params); - - expect(result.type).toBe('Feature'); - expect(result.geometry.type).toBe('LineString'); - // First and last coordinates should be swapped in longitude - const origFirst = input.geometry.coordinates[0][0]; - const origLast = input.geometry.coordinates[2][0]; - const center = (origFirst + origLast + input.geometry.coordinates[1][0]) / 3; - expect(Math.abs(result.geometry.coordinates[0][0] - (2 * center - origFirst))).toBeLessThan(0.0001); - }); - }); - - describe('flipVertical', () => { - test('should flip features vertically', () => { - const input = JSON.parse(JSON.stringify(sampleLineString)); - const params = { axis: 'latitude' }; - const result = window.ToolVault.tools.flipVertical(input, params); - - expect(result.type).toBe('Feature'); - expect(result.geometry.type).toBe('LineString'); - }); - }); -}); - -describe('Analysis Tools', () => { - describe('calculateSpeedSeries', () => { - test('should calculate speed series from GPS track', () => { - const input = sampleTrackWithTimestamps; - const params = { time_unit: 'seconds' }; - const result = window.ToolVault.tools.calculateSpeedSeries(input, params); - - expect(Array.isArray(result)).toBe(true); - expect(result.length).toBe(2); // One less than number of points - expect(result[0]).toHaveProperty('time'); - expect(result[0]).toHaveProperty('speed'); - expect(typeof result[0].speed).toBe('number'); - }); - - test('should handle different time units', () => { - const input = sampleTrackWithTimestamps; - const resultSeconds = window.ToolVault.tools.calculateSpeedSeries(input, { time_unit: 'seconds' }); - const resultMinutes = window.ToolVault.tools.calculateSpeedSeries(input, { time_unit: 'minutes' }); - - expect(resultMinutes[0].speed).toBeCloseTo(resultSeconds[0].speed * 60, 2); - }); - }); - - describe('calculateDirectionSeries', () => { - test('should calculate direction series from GPS track', () => { - const input = sampleTrackWithTimestamps; - const params = { smoothing: false }; - const result = window.ToolVault.tools.calculateDirectionSeries(input, params); - - expect(Array.isArray(result)).toBe(true); - expect(result.length).toBe(2); - expect(result[0]).toHaveProperty('time'); - expect(result[0]).toHaveProperty('direction'); - expect(result[0].direction).toBeGreaterThanOrEqual(0); - expect(result[0].direction).toBeLessThanOrEqual(360); - }); - - test('should apply smoothing when requested', () => { - const input = sampleTrackWithTimestamps; - const resultNoSmooth = window.ToolVault.tools.calculateDirectionSeries(input, { smoothing: false }); - const resultSmooth = window.ToolVault.tools.calculateDirectionSeries(input, { smoothing: true, window_size: 3 }); - - expect(resultSmooth.length).toBe(resultNoSmooth.length); - }); - }); -}); - -describe('Statistics Tools', () => { - describe('calculateAverageSpeed', () => { - test('should calculate average speed from GPS track', () => { - const input = sampleTrackWithTimestamps; - const params = { time_unit: 'seconds' }; - const result = window.ToolVault.tools.calculateAverageSpeed(input, params); - - expect(typeof result).toBe('number'); - expect(result).toBeGreaterThan(0); - }); - }); - - describe('createSpeedHistogram', () => { - test('should create speed histogram from GPS track', () => { - const input = sampleTrackWithTimestamps; - const params = { interval_minutes: 1, bins: 10 }; - const result = window.ToolVault.tools.createSpeedHistogram(input, params); - - expect(result).toHaveProperty('bins'); - expect(result).toHaveProperty('counts'); - expect(result).toHaveProperty('min'); - expect(result).toHaveProperty('max'); - expect(Array.isArray(result.bins)).toBe(true); - expect(Array.isArray(result.counts)).toBe(true); - }); - }); -}); - -describe('Processing Tools', () => { - describe('smoothPolyline', () => { - test('should smooth polyline with moving average', () => { - const input = sampleLineString; - const params = { algorithm: 'moving_average', window_size: 3 }; - const result = window.ToolVault.tools.smoothPolyline(input, params); - - expect(result.type).toBe('Feature'); - expect(result.geometry.type).toBe('LineString'); - expect(result.geometry.coordinates).toHaveLength(input.geometry.coordinates.length); - }); - - test('should smooth polyline with gaussian', () => { - const input = sampleLineString; - const params = { algorithm: 'gaussian', window_size: 3 }; - const result = window.ToolVault.tools.smoothPolyline(input, params); - - expect(result.type).toBe('Feature'); - expect(result.geometry.type).toBe('LineString'); - expect(result.geometry.coordinates).toHaveLength(input.geometry.coordinates.length); - }); - }); -}); - -describe('I/O Tools', () => { - describe('importREP', () => { - test('should import REP format to GeoJSON', () => { - const repContent = `# Test REP file -51.5074,-0.1276,0,2024-01-18T10:00:00Z -51.5076,-0.1278,0,2024-01-18T10:00:05Z -51.5078,-0.1280,0,2024-01-18T10:00:10Z`; - - const result = window.ToolVault.tools.importREP(repContent, {}); - - expect(result.type).toBe('FeatureCollection'); - expect(result.features).toHaveLength(1); - expect(result.features[0].geometry.type).toBe('LineString'); - expect(result.features[0].geometry.coordinates).toHaveLength(3); - }); - - test('should handle single point', () => { - const repContent = '51.5074,-0.1276,0,2024-01-18T10:00:00Z'; - const result = window.ToolVault.tools.importREP(repContent, {}); - - expect(result.features[0].geometry.type).toBe('Point'); - }); - }); - - describe('exportREP', () => { - test('should export GeoJSON to REP format', () => { - const input = sampleLineString; - const params = { precision: 4 }; - const result = window.ToolVault.tools.exportREP(input, params); - - expect(typeof result).toBe('string'); - expect(result).toContain('51.5074'); - expect(result).toContain('-0.1276'); - const lines = result.split('\n'); - expect(lines.length).toBeGreaterThan(3); // Headers + coordinates - }); - - test('should export FeatureCollection', () => { - const input = sampleFeatureCollection; - const result = window.ToolVault.tools.exportREP(input, { precision: 6 }); - - expect(typeof result).toBe('string'); - expect(result).toContain('REP Format Export'); - }); - }); - - describe('exportCSV', () => { - test('should export GeoJSON to CSV with separate coordinates', () => { - const input = sampleLineString; - const params = { include_properties: true, coordinate_format: 'separate' }; - const result = window.ToolVault.tools.exportCSV(input, params); - - expect(typeof result).toBe('string'); - const lines = result.split('\n'); - expect(lines[0]).toContain('longitude'); - expect(lines[0]).toContain('latitude'); - expect(lines.length).toBe(4); // Header + 3 coordinates - }); - - test('should export to WKT format', () => { - const input = sampleLineString; - const params = { include_properties: false, coordinate_format: 'wkt' }; - const result = window.ToolVault.tools.exportCSV(input, params); - - expect(result).toContain('LINESTRING'); - const lines = result.split('\n'); - expect(lines.length).toBe(2); // Header + 1 WKT row - }); - - test('should handle FeatureCollection', () => { - const input = sampleFeatureCollection; - const result = window.ToolVault.tools.exportCSV(input, { coordinate_format: 'wkt' }); - - expect(result).toContain('POINT'); - expect(result).toContain('LINESTRING'); - }); - }); -}); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/analysis/direction-series.js b/examples/javascript-bundle/tools/analysis/direction-series.js deleted file mode 100644 index 52cfa98..0000000 --- a/examples/javascript-bundle/tools/analysis/direction-series.js +++ /dev/null @@ -1,77 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.calculateDirectionSeries = function(input, params) { - const { smoothing = false, window_size = 3 } = params || {}; - - // Calculate bearing between two points - function calculateBearing(lat1, lon1, lat2, lon2) { - const φ1 = lat1 * Math.PI / 180; - const φ2 = lat2 * Math.PI / 180; - const Δλ = (lon2 - lon1) * Math.PI / 180; - - const y = Math.sin(Δλ) * Math.cos(φ2); - const x = Math.cos(φ1) * Math.sin(φ2) - - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ); - - const θ = Math.atan2(y, x); - - return ((θ * 180 / Math.PI) + 360) % 360; // Convert to degrees and normalize to 0-360 - } - - // Extract coordinates and timestamps - let coordinates = []; - let timestamps = []; - - if (input.type === 'Feature' && input.geometry.type === 'LineString') { - coordinates = input.geometry.coordinates; - timestamps = input.geometry.properties?.timestamps || []; - } else if (input.type === 'LineString') { - coordinates = input.coordinates; - timestamps = input.properties?.timestamps || []; - } - - // Calculate directions - const rawSeries = []; - - for (let i = 1; i < coordinates.length && i < timestamps.length; i++) { - const coord1 = coordinates[i - 1]; - const coord2 = coordinates[i]; - - const bearing = calculateBearing(coord1[1], coord1[0], coord2[1], coord2[0]); - - rawSeries.push({ - time: timestamps[i], - direction: bearing - }); - } - - // Apply smoothing if requested - if (smoothing && window_size > 1) { - const smoothedSeries = []; - const halfWindow = Math.floor(window_size / 2); - - for (let i = 0; i < rawSeries.length; i++) { - let sum = 0; - let count = 0; - - for (let j = Math.max(0, i - halfWindow); - j <= Math.min(rawSeries.length - 1, i + halfWindow); - j++) { - sum += rawSeries[j].direction; - count++; - } - - smoothedSeries.push({ - time: rawSeries[i].time, - direction: sum / count - }); - } - - return smoothedSeries; - } - - return rawSeries; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/analysis/speed-series.js b/examples/javascript-bundle/tools/analysis/speed-series.js deleted file mode 100644 index 7040c8b..0000000 --- a/examples/javascript-bundle/tools/analysis/speed-series.js +++ /dev/null @@ -1,70 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.calculateSpeedSeries = function(input, params) { - const { time_unit = 'seconds' } = params || {}; - - // Haversine formula to calculate distance between two points - function haversineDistance(lat1, lon1, lat2, lon2) { - const R = 6371000; // Earth radius in meters - const φ1 = lat1 * Math.PI / 180; - const φ2 = lat2 * Math.PI / 180; - const Δφ = (lat2 - lat1) * Math.PI / 180; - const Δλ = (lon2 - lon1) * Math.PI / 180; - - const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + - Math.cos(φ1) * Math.cos(φ2) * - Math.sin(Δλ/2) * Math.sin(Δλ/2); - const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); - - return R * c; // Distance in meters - } - - // Extract coordinates and timestamps - let coordinates = []; - let timestamps = []; - - if (input.type === 'Feature' && input.geometry.type === 'LineString') { - coordinates = input.geometry.coordinates; - timestamps = input.geometry.properties?.timestamps || []; - } else if (input.type === 'LineString') { - coordinates = input.coordinates; - timestamps = input.properties?.timestamps || []; - } - - // Calculate speeds - const series = []; - - for (let i = 1; i < coordinates.length && i < timestamps.length; i++) { - const coord1 = coordinates[i - 1]; - const coord2 = coordinates[i]; - const time1 = new Date(timestamps[i - 1]); - const time2 = new Date(timestamps[i]); - - // Calculate distance in meters - const distance = haversineDistance(coord1[1], coord1[0], coord2[1], coord2[0]); - - // Calculate time difference in seconds - const timeDiff = (time2 - time1) / 1000; - - if (timeDiff > 0) { - // Calculate speed based on requested unit - let speed = distance / timeDiff; // m/s - - if (time_unit === 'minutes') { - speed = speed * 60; // m/min - } else if (time_unit === 'hours') { - speed = speed * 3600; // m/h - } - - series.push({ - time: timestamps[i], - speed: speed - }); - } - } - - return series; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/io/export-csv.js b/examples/javascript-bundle/tools/io/export-csv.js deleted file mode 100644 index 11773b9..0000000 --- a/examples/javascript-bundle/tools/io/export-csv.js +++ /dev/null @@ -1,141 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.exportCSV = function(input, params) { - const { include_properties = true, coordinate_format = 'separate' } = params || {}; - - const rows = []; - const headers = []; - - // Helper function to escape CSV values - function escapeCSV(value) { - if (value === null || value === undefined) { - return ''; - } - value = String(value); - if (value.includes(',') || value.includes('"') || value.includes('\n')) { - return '"' + value.replace(/"/g, '""') + '"'; - } - return value; - } - - // Helper function to convert geometry to WKT - function geometryToWKT(geometry) { - if (geometry.type === 'Point') { - return `POINT(${geometry.coordinates[0]} ${geometry.coordinates[1]})`; - } else if (geometry.type === 'LineString') { - const coords = geometry.coordinates - .map(c => `${c[0]} ${c[1]}`) - .join(','); - return `LINESTRING(${coords})`; - } else if (geometry.type === 'Polygon') { - const rings = geometry.coordinates - .map(ring => { - const coords = ring.map(c => `${c[0]} ${c[1]}`).join(','); - return `(${coords})`; - }) - .join(','); - return `POLYGON${rings}`; - } - return ''; - } - - // Helper function to flatten geometry coordinates - function flattenCoordinates(geometry) { - const points = []; - - if (geometry.type === 'Point') { - points.push(geometry.coordinates); - } else if (geometry.type === 'LineString') { - points.push(...geometry.coordinates); - } else if (geometry.type === 'Polygon') { - points.push(...geometry.coordinates[0]); // Only outer ring - } - - return points; - } - - // Process features - let features = []; - - if (input.type === 'FeatureCollection') { - features = input.features; - } else if (input.type === 'Feature') { - features = [input]; - } else if (input.type && input.coordinates) { - // Direct geometry - features = [{ - type: 'Feature', - properties: {}, - geometry: input - }]; - } - - // Collect all property keys for headers - const propertyKeys = new Set(); - - if (include_properties) { - features.forEach(feature => { - if (feature.properties) { - Object.keys(feature.properties).forEach(key => { - propertyKeys.add(key); - }); - } - }); - } - - // Build headers - if (coordinate_format === 'wkt') { - headers.push('geometry'); - } else { - headers.push('longitude', 'latitude'); - } - - if (include_properties) { - headers.push(...Array.from(propertyKeys)); - } - - rows.push(headers.map(escapeCSV).join(',')); - - // Process each feature - features.forEach(feature => { - if (!feature.geometry) { - return; - } - - if (coordinate_format === 'wkt') { - // Single row per feature with WKT geometry - const row = []; - row.push(escapeCSV(geometryToWKT(feature.geometry))); - - if (include_properties && feature.properties) { - propertyKeys.forEach(key => { - row.push(escapeCSV(feature.properties[key])); - }); - } - - rows.push(row.join(',')); - } else { - // One row per coordinate pair - const coordinates = flattenCoordinates(feature.geometry); - - coordinates.forEach(coord => { - const row = []; - row.push(escapeCSV(coord[0])); // longitude - row.push(escapeCSV(coord[1])); // latitude - - if (include_properties && feature.properties) { - propertyKeys.forEach(key => { - row.push(escapeCSV(feature.properties[key])); - }); - } - - rows.push(row.join(',')); - }); - } - }); - - return rows.join('\n'); - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/io/export-rep.js b/examples/javascript-bundle/tools/io/export-rep.js deleted file mode 100644 index 12f3d6e..0000000 --- a/examples/javascript-bundle/tools/io/export-rep.js +++ /dev/null @@ -1,87 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.exportREP = function(input, params) { - const { precision = 6 } = params || {}; - - // REP format output - let output = []; - - // Add header comment - output.push('# REP Format Export from ToolVault'); - output.push('# Format: lat,lon[,altitude][,timestamp]'); - output.push(''); - - // Helper function to format number with precision - function formatNumber(num) { - return Number(num).toFixed(precision); - } - - // Helper function to extract coordinates from geometry - function extractCoordinates(geometry) { - const coords = []; - const timestamps = geometry.properties?.timestamps || []; - - if (geometry.type === 'Point') { - const lon = formatNumber(geometry.coordinates[0]); - const lat = formatNumber(geometry.coordinates[1]); - const alt = geometry.coordinates[2] ? formatNumber(geometry.coordinates[2]) : '0'; - const time = timestamps[0] || ''; - coords.push(`${lat},${lon},${alt},${time}`); - } else if (geometry.type === 'LineString') { - geometry.coordinates.forEach((coord, index) => { - const lon = formatNumber(coord[0]); - const lat = formatNumber(coord[1]); - const alt = coord[2] ? formatNumber(coord[2]) : '0'; - const time = timestamps[index] || ''; - coords.push(`${lat},${lon},${alt},${time}`); - }); - } else if (geometry.type === 'Polygon') { - // Export only the outer ring - geometry.coordinates[0].forEach((coord, index) => { - const lon = formatNumber(coord[0]); - const lat = formatNumber(coord[1]); - const alt = coord[2] ? formatNumber(coord[2]) : '0'; - coords.push(`${lat},${lon},${alt},`); - }); - } - - return coords; - } - - // Process input based on type - if (input.type === 'FeatureCollection') { - input.features.forEach((feature, featureIndex) => { - if (featureIndex > 0) { - output.push(''); // Empty line between features - } - - // Add feature name/description if available - if (feature.properties?.name) { - output.push(`# ${feature.properties.name}`); - } - - if (feature.geometry) { - const coords = extractCoordinates(feature.geometry); - output = output.concat(coords); - } - }); - } else if (input.type === 'Feature') { - if (input.properties?.name) { - output.push(`# ${input.properties.name}`); - } - - if (input.geometry) { - const coords = extractCoordinates(input.geometry); - output = output.concat(coords); - } - } else if (input.type && input.coordinates) { - // Direct geometry object - const coords = extractCoordinates(input); - output = output.concat(coords); - } - - return output.join('\n'); - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/io/import-rep.js b/examples/javascript-bundle/tools/io/import-rep.js deleted file mode 100644 index fe5863d..0000000 --- a/examples/javascript-bundle/tools/io/import-rep.js +++ /dev/null @@ -1,81 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.importREP = function(input, params) { - const { encoding = 'utf-8' } = params || {}; - - // REP format is a simple text format with lines of coordinates - // Format: lat,lon[,altitude][,time] - // Lines starting with # are comments - // First non-comment line may contain metadata - - const lines = input.split('\n'); - const features = []; - const coordinates = []; - const timestamps = []; - let metadata = {}; - - for (let line of lines) { - line = line.trim(); - - // Skip empty lines and comments - if (!line || line.startsWith('#')) { - continue; - } - - // Parse line as coordinate data - const parts = line.split(',').map(p => p.trim()); - - if (parts.length >= 2) { - const lat = parseFloat(parts[0]); - const lon = parseFloat(parts[1]); - - if (!isNaN(lat) && !isNaN(lon)) { - coordinates.push([lon, lat]); // GeoJSON uses [lon, lat] - - // Check for timestamp (4th field) - if (parts.length >= 4 && parts[3]) { - timestamps.push(parts[3]); - } - } else if (coordinates.length === 0) { - // First non-coordinate line might be metadata - try { - metadata = { description: line }; - } catch (e) { - // Ignore parsing errors - } - } - } - } - - // Create GeoJSON output - if (coordinates.length > 0) { - const feature = { - type: 'Feature', - properties: { - ...metadata, - source: 'REP import' - }, - geometry: { - type: coordinates.length === 1 ? 'Point' : 'LineString', - coordinates: coordinates.length === 1 ? coordinates[0] : coordinates - } - }; - - // Add timestamps if available - if (timestamps.length > 0) { - feature.geometry.properties = { - timestamps: timestamps - }; - } - - features.push(feature); - } - - return { - type: 'FeatureCollection', - features: features - }; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/processing/smooth-polyline.js b/examples/javascript-bundle/tools/processing/smooth-polyline.js deleted file mode 100644 index 2516d76..0000000 --- a/examples/javascript-bundle/tools/processing/smooth-polyline.js +++ /dev/null @@ -1,91 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.smoothPolyline = function(input, params) { - const { algorithm = 'moving_average', window_size = 3 } = params || {}; - - // Extract coordinates - let coordinates = []; - let inputType = null; - - if (input.type === 'Feature' && input.geometry.type === 'LineString') { - coordinates = input.geometry.coordinates; - inputType = 'Feature'; - } else if (input.type === 'LineString') { - coordinates = input.coordinates; - inputType = 'LineString'; - } - - if (coordinates.length < 2) { - return input; // Not enough points to smooth - } - - // Deep clone the input - const result = JSON.parse(JSON.stringify(input)); - - // Apply smoothing based on algorithm - let smoothedCoords = []; - - if (algorithm === 'moving_average') { - // Moving average smoothing - const halfWindow = Math.floor(window_size / 2); - - for (let i = 0; i < coordinates.length; i++) { - let sumLon = 0; - let sumLat = 0; - let count = 0; - - for (let j = Math.max(0, i - halfWindow); - j <= Math.min(coordinates.length - 1, i + halfWindow); - j++) { - sumLon += coordinates[j][0]; - sumLat += coordinates[j][1]; - count++; - } - - smoothedCoords.push([sumLon / count, sumLat / count]); - } - } else if (algorithm === 'gaussian') { - // Gaussian smoothing - const sigma = window_size / 3; // Standard deviation - - // Generate Gaussian weights - function gaussianWeight(distance, sigma) { - return Math.exp(-(distance * distance) / (2 * sigma * sigma)); - } - - for (let i = 0; i < coordinates.length; i++) { - let weightedSumLon = 0; - let weightedSumLat = 0; - let totalWeight = 0; - - for (let j = 0; j < coordinates.length; j++) { - const distance = Math.abs(i - j); - const weight = gaussianWeight(distance, sigma); - - weightedSumLon += coordinates[j][0] * weight; - weightedSumLat += coordinates[j][1] * weight; - totalWeight += weight; - } - - smoothedCoords.push([ - weightedSumLon / totalWeight, - weightedSumLat / totalWeight - ]); - } - } else { - // Default to original coordinates if algorithm not recognized - smoothedCoords = coordinates; - } - - // Update the result with smoothed coordinates - if (inputType === 'Feature') { - result.geometry.coordinates = smoothedCoords; - } else if (inputType === 'LineString') { - result.coordinates = smoothedCoords; - } - - return result; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/statistics/average-speed.js b/examples/javascript-bundle/tools/statistics/average-speed.js deleted file mode 100644 index e2c3ea7..0000000 --- a/examples/javascript-bundle/tools/statistics/average-speed.js +++ /dev/null @@ -1,68 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.calculateAverageSpeed = function(input, params) { - const { time_unit = 'seconds' } = params || {}; - - // Haversine formula to calculate distance between two points - function haversineDistance(lat1, lon1, lat2, lon2) { - const R = 6371000; // Earth radius in meters - const φ1 = lat1 * Math.PI / 180; - const φ2 = lat2 * Math.PI / 180; - const Δφ = (lat2 - lat1) * Math.PI / 180; - const Δλ = (lon2 - lon1) * Math.PI / 180; - - const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + - Math.cos(φ1) * Math.cos(φ2) * - Math.sin(Δλ/2) * Math.sin(Δλ/2); - const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); - - return R * c; // Distance in meters - } - - // Extract coordinates and timestamps - let coordinates = []; - let timestamps = []; - - if (input.type === 'Feature' && input.geometry.type === 'LineString') { - coordinates = input.geometry.coordinates; - timestamps = input.geometry.properties?.timestamps || []; - } else if (input.type === 'LineString') { - coordinates = input.coordinates; - timestamps = input.properties?.timestamps || []; - } - - // Calculate total distance and time - let totalDistance = 0; - let totalTime = 0; - - for (let i = 1; i < coordinates.length && i < timestamps.length; i++) { - const coord1 = coordinates[i - 1]; - const coord2 = coordinates[i]; - const time1 = new Date(timestamps[i - 1]); - const time2 = new Date(timestamps[i]); - - // Calculate distance in meters - totalDistance += haversineDistance(coord1[1], coord1[0], coord2[1], coord2[0]); - - // Calculate time difference in seconds - totalTime += (time2 - time1) / 1000; - } - - if (totalTime === 0) { - return 0; - } - - // Calculate average speed based on requested unit - let averageSpeed = totalDistance / totalTime; // m/s - - if (time_unit === 'minutes') { - averageSpeed = averageSpeed * 60; // m/min - } else if (time_unit === 'hours') { - averageSpeed = averageSpeed * 3600; // m/h - } - - return averageSpeed; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/statistics/speed-histogram.js b/examples/javascript-bundle/tools/statistics/speed-histogram.js deleted file mode 100644 index f4fb793..0000000 --- a/examples/javascript-bundle/tools/statistics/speed-histogram.js +++ /dev/null @@ -1,82 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.createSpeedHistogram = function(input, params) { - const { interval_minutes = 1, bins = 20 } = params || {}; - - // Use the calculateSpeedSeries function to get speeds - const speedSeries = window.ToolVault.tools.calculateSpeedSeries(input, { time_unit: 'seconds' }); - - if (!speedSeries || speedSeries.length === 0) { - return { - bins: [], - counts: [], - min: 0, - max: 0 - }; - } - - // Group speeds by time intervals - const intervalMs = interval_minutes * 60 * 1000; - const intervalGroups = {}; - - speedSeries.forEach(point => { - const time = new Date(point.time); - const intervalKey = Math.floor(time.getTime() / intervalMs) * intervalMs; - - if (!intervalGroups[intervalKey]) { - intervalGroups[intervalKey] = []; - } - intervalGroups[intervalKey].push(point.speed); - }); - - // Calculate average speed for each interval - const intervalSpeeds = Object.values(intervalGroups).map(speeds => { - const sum = speeds.reduce((a, b) => a + b, 0); - return sum / speeds.length; - }); - - if (intervalSpeeds.length === 0) { - return { - bins: [], - counts: [], - min: 0, - max: 0 - }; - } - - // Find min and max speeds - const minSpeed = Math.min(...intervalSpeeds); - const maxSpeed = Math.max(...intervalSpeeds); - - // Create histogram bins - const binWidth = (maxSpeed - minSpeed) / bins; - const histogram = { - bins: [], - counts: new Array(bins).fill(0), - min: minSpeed, - max: maxSpeed, - binWidth: binWidth - }; - - // Create bin edges - for (let i = 0; i < bins; i++) { - histogram.bins.push({ - min: minSpeed + i * binWidth, - max: minSpeed + (i + 1) * binWidth, - center: minSpeed + (i + 0.5) * binWidth - }); - } - - // Count speeds in each bin - intervalSpeeds.forEach(speed => { - const binIndex = Math.min(Math.floor((speed - minSpeed) / binWidth), bins - 1); - if (binIndex >= 0 && binIndex < bins) { - histogram.counts[binIndex]++; - } - }); - - return histogram; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/transform/flip-horizontal.js b/examples/javascript-bundle/tools/transform/flip-horizontal.js deleted file mode 100644 index 35c48d1..0000000 --- a/examples/javascript-bundle/tools/transform/flip-horizontal.js +++ /dev/null @@ -1,87 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.flipHorizontal = function(input, params) { - const { axis = 'longitude' } = params || {}; - - // Deep clone the input - const result = JSON.parse(JSON.stringify(input)); - - // Find the center point for flipping - let center = 0; - let coordCount = 0; - - // Helper to collect all coordinates - function collectCoordinates(coords, axisIndex) { - if (typeof coords[0] === 'number') { - center += coords[axisIndex]; - coordCount++; - } else { - coords.forEach(c => collectCoordinates(c, axisIndex)); - } - } - - // Helper to flip coordinates - function flipCoordinate(coord, axisIndex, centerValue) { - const newCoord = [...coord]; - newCoord[axisIndex] = 2 * centerValue - coord[axisIndex]; - return newCoord; - } - - // Helper to flip coordinates recursively - function flipCoordinates(coords, axisIndex, centerValue) { - if (typeof coords[0] === 'number') { - return flipCoordinate(coords, axisIndex, centerValue); - } - return coords.map(c => flipCoordinates(c, axisIndex, centerValue)); - } - - // Determine axis index (0 for longitude, 1 for latitude) - const axisIndex = axis === 'latitude' ? 1 : 0; - - // Collect all coordinates to find center - if (result.type === 'FeatureCollection') { - result.features.forEach(feature => { - if (feature.geometry && feature.geometry.coordinates) { - collectCoordinates(feature.geometry.coordinates, axisIndex); - } - }); - } else if (result.type === 'Feature') { - if (result.geometry && result.geometry.coordinates) { - collectCoordinates(result.geometry.coordinates, axisIndex); - } - } else if (result.coordinates) { - collectCoordinates(result.coordinates, axisIndex); - } - - // Calculate center - center = coordCount > 0 ? center / coordCount : 0; - - // Apply flipping - if (result.type === 'FeatureCollection') { - result.features = result.features.map(feature => { - if (feature.geometry && feature.geometry.coordinates) { - feature.geometry.coordinates = flipCoordinates( - feature.geometry.coordinates, - axisIndex, - center - ); - } - return feature; - }); - } else if (result.type === 'Feature') { - if (result.geometry && result.geometry.coordinates) { - result.geometry.coordinates = flipCoordinates( - result.geometry.coordinates, - axisIndex, - center - ); - } - } else if (result.coordinates) { - result.coordinates = flipCoordinates(result.coordinates, axisIndex, center); - } - - return result; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/transform/flip-vertical.js b/examples/javascript-bundle/tools/transform/flip-vertical.js deleted file mode 100644 index e4926f9..0000000 --- a/examples/javascript-bundle/tools/transform/flip-vertical.js +++ /dev/null @@ -1,87 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.flipVertical = function(input, params) { - const { axis = 'latitude' } = params || {}; - - // Deep clone the input - const result = JSON.parse(JSON.stringify(input)); - - // Find the center point for flipping - let center = 0; - let coordCount = 0; - - // Helper to collect all coordinates - function collectCoordinates(coords, axisIndex) { - if (typeof coords[0] === 'number') { - center += coords[axisIndex]; - coordCount++; - } else { - coords.forEach(c => collectCoordinates(c, axisIndex)); - } - } - - // Helper to flip coordinates - function flipCoordinate(coord, axisIndex, centerValue) { - const newCoord = [...coord]; - newCoord[axisIndex] = 2 * centerValue - coord[axisIndex]; - return newCoord; - } - - // Helper to flip coordinates recursively - function flipCoordinates(coords, axisIndex, centerValue) { - if (typeof coords[0] === 'number') { - return flipCoordinate(coords, axisIndex, centerValue); - } - return coords.map(c => flipCoordinates(c, axisIndex, centerValue)); - } - - // Determine axis index (0 for longitude, 1 for latitude) - const axisIndex = axis === 'longitude' ? 0 : 1; - - // Collect all coordinates to find center - if (result.type === 'FeatureCollection') { - result.features.forEach(feature => { - if (feature.geometry && feature.geometry.coordinates) { - collectCoordinates(feature.geometry.coordinates, axisIndex); - } - }); - } else if (result.type === 'Feature') { - if (result.geometry && result.geometry.coordinates) { - collectCoordinates(result.geometry.coordinates, axisIndex); - } - } else if (result.coordinates) { - collectCoordinates(result.coordinates, axisIndex); - } - - // Calculate center - center = coordCount > 0 ? center / coordCount : 0; - - // Apply flipping - if (result.type === 'FeatureCollection') { - result.features = result.features.map(feature => { - if (feature.geometry && feature.geometry.coordinates) { - feature.geometry.coordinates = flipCoordinates( - feature.geometry.coordinates, - axisIndex, - center - ); - } - return feature; - }); - } else if (result.type === 'Feature') { - if (result.geometry && result.geometry.coordinates) { - result.geometry.coordinates = flipCoordinates( - result.geometry.coordinates, - axisIndex, - center - ); - } - } else if (result.coordinates) { - result.coordinates = flipCoordinates(result.coordinates, axisIndex, center); - } - - return result; - }; -})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/transform/translate.js b/examples/javascript-bundle/tools/transform/translate.js deleted file mode 100644 index 3f038fc..0000000 --- a/examples/javascript-bundle/tools/transform/translate.js +++ /dev/null @@ -1,56 +0,0 @@ -(function() { - window.ToolVault = window.ToolVault || {}; - window.ToolVault.tools = window.ToolVault.tools || {}; - - window.ToolVault.tools.translateFeatures = function(input, params) { - const { direction = 0, distance = 100 } = params || {}; - - // Convert direction to radians - const directionRad = (direction * Math.PI) / 180; - - // Calculate offset in degrees (approximation for small distances) - // Earth radius in meters - const earthRadius = 6371000; - const latOffset = (distance * Math.cos(directionRad)) / earthRadius * (180 / Math.PI); - const lonOffset = (distance * Math.sin(directionRad)) / earthRadius * (180 / Math.PI); - - // Helper function to translate a coordinate - function translateCoordinate(coord) { - const avgLat = coord[1]; - const lonCorrection = Math.cos(avgLat * Math.PI / 180); - return [ - coord[0] + lonOffset / lonCorrection, - coord[1] + latOffset - ]; - } - - // Helper function to translate coordinates recursively - function translateCoordinates(coords) { - if (typeof coords[0] === 'number') { - return translateCoordinate(coords); - } - return coords.map(translateCoordinates); - } - - // Deep clone the input - const result = JSON.parse(JSON.stringify(input)); - - // Handle different GeoJSON types - if (result.type === 'FeatureCollection') { - result.features = result.features.map(feature => { - if (feature.geometry && feature.geometry.coordinates) { - feature.geometry.coordinates = translateCoordinates(feature.geometry.coordinates); - } - return feature; - }); - } else if (result.type === 'Feature') { - if (result.geometry && result.geometry.coordinates) { - result.geometry.coordinates = translateCoordinates(result.geometry.coordinates); - } - } else if (result.coordinates) { - result.coordinates = translateCoordinates(result.coordinates); - } - - return result; - }; -})(); \ No newline at end of file From 83d585dd4f2b78a549d23fc925f867d9271ceafa Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 17:10:13 +0100 Subject: [PATCH 09/18] Initial JS bundle --- .gitignore | 2 + examples/javascript-bundle/index.json | 364 ++ examples/javascript-bundle/package-lock.json | 5312 +++++++++++++++++ examples/javascript-bundle/package.json | 36 + .../tests/analysis/direction-series.test.js | 149 + .../tests/analysis/speed-series.test.js | 117 + examples/javascript-bundle/tests/helpers.js | 102 + .../tests/integration/tool-loading.test.js | 244 + .../tests/io/export-csv.test.js | 230 + .../tests/io/export-rep.test.js | 328 + .../tests/io/import-rep.test.js | 230 + .../tests/processing/smooth-polyline.test.js | 229 + examples/javascript-bundle/tests/setup.js | 31 + .../tests/statistics/average-speed.test.js | 163 + .../tests/statistics/speed-histogram.test.js | 148 + .../tests/transform/flip.test.js | 114 + .../tests/transform/translate.test.js | 91 + .../tools/analysis/direction-series.js | 77 + .../tools/analysis/speed-series.js | 70 + .../javascript-bundle/tools/io/export-csv.js | 165 + .../javascript-bundle/tools/io/export-rep.js | 172 + .../javascript-bundle/tools/io/import-rep.js | 176 + .../tools/processing/smooth-polyline.js | 108 + .../tools/statistics/average-speed.js | 68 + .../tools/statistics/speed-histogram.js | 59 + .../tools/transform/flip-horizontal.js | 99 + .../tools/transform/flip-vertical.js | 10 + .../tools/transform/translate.js | 70 + 28 files changed, 8964 insertions(+) create mode 100644 examples/javascript-bundle/index.json create mode 100644 examples/javascript-bundle/package-lock.json create mode 100644 examples/javascript-bundle/package.json create mode 100644 examples/javascript-bundle/tests/analysis/direction-series.test.js create mode 100644 examples/javascript-bundle/tests/analysis/speed-series.test.js create mode 100644 examples/javascript-bundle/tests/helpers.js create mode 100644 examples/javascript-bundle/tests/integration/tool-loading.test.js create mode 100644 examples/javascript-bundle/tests/io/export-csv.test.js create mode 100644 examples/javascript-bundle/tests/io/export-rep.test.js create mode 100644 examples/javascript-bundle/tests/io/import-rep.test.js create mode 100644 examples/javascript-bundle/tests/processing/smooth-polyline.test.js create mode 100644 examples/javascript-bundle/tests/setup.js create mode 100644 examples/javascript-bundle/tests/statistics/average-speed.test.js create mode 100644 examples/javascript-bundle/tests/statistics/speed-histogram.test.js create mode 100644 examples/javascript-bundle/tests/transform/flip.test.js create mode 100644 examples/javascript-bundle/tests/transform/translate.test.js create mode 100644 examples/javascript-bundle/tools/analysis/direction-series.js create mode 100644 examples/javascript-bundle/tools/analysis/speed-series.js create mode 100644 examples/javascript-bundle/tools/io/export-csv.js create mode 100644 examples/javascript-bundle/tools/io/export-rep.js create mode 100644 examples/javascript-bundle/tools/io/import-rep.js create mode 100644 examples/javascript-bundle/tools/processing/smooth-polyline.js create mode 100644 examples/javascript-bundle/tools/statistics/average-speed.js create mode 100644 examples/javascript-bundle/tools/statistics/speed-histogram.js create mode 100644 examples/javascript-bundle/tools/transform/flip-horizontal.js create mode 100644 examples/javascript-bundle/tools/transform/flip-vertical.js create mode 100644 examples/javascript-bundle/tools/transform/translate.js diff --git a/.gitignore b/.gitignore index 68491f4..4902542 100644 --- a/.gitignore +++ b/.gitignore @@ -208,3 +208,5 @@ __marimo__/ client/coverage/ client/test-results/ client/playwright-report/ +examples/javascript-bundle/node_modules/ +examples/javascript-bundle/coverage/ diff --git a/examples/javascript-bundle/index.json b/examples/javascript-bundle/index.json new file mode 100644 index 0000000..1564185 --- /dev/null +++ b/examples/javascript-bundle/index.json @@ -0,0 +1,364 @@ +{ + "name": "JavaScript Mock Tool Bundle", + "version": "0.1.0", + "description": "A comprehensive collection of mock analysis tools for testing ToolVault's JavaScript toolset architecture", + "author": "ToolVault Development Team", + "license": "MIT", + "bundle_format": "javascript", + "runtime": "browser", + "tools": [ + { + "id": "translate", + "name": "Translate Features", + "category": "transform", + "description": "Translate geometric features by distance and direction or simple dx/dy offsets", + "input_types": ["Feature", "FeatureCollection", "geometry"], + "output_types": ["Feature", "FeatureCollection", "geometry"], + "parameters": [ + { + "name": "distance", + "type": "number", + "default": 0, + "description": "Distance to translate (meters)" + }, + { + "name": "direction", + "type": "number", + "default": 0, + "description": "Direction in degrees (0=North, 90=East)" + }, + { + "name": "units", + "type": "string", + "default": "meters", + "enum": ["meters", "kilometers"], + "description": "Distance units" + }, + { + "name": "dx", + "type": "number", + "default": 0, + "description": "Simple offset in longitude (degrees)" + }, + { + "name": "dy", + "type": "number", + "default": 0, + "description": "Simple offset in latitude (degrees)" + } + ], + "examples": [ + { + "name": "Move 1km North", + "parameters": { "distance": 1000, "direction": 0, "units": "meters" } + }, + { + "name": "Simple offset", + "parameters": { "dx": 0.01, "dy": 0.01 } + } + ] + }, + { + "id": "flip-horizontal", + "name": "Flip Horizontal", + "category": "transform", + "description": "Flip features horizontally around the longitude or latitude axis", + "input_types": ["Feature", "FeatureCollection", "geometry"], + "output_types": ["Feature", "FeatureCollection", "geometry"], + "parameters": [ + { + "name": "axis", + "type": "string", + "default": "longitude", + "enum": ["longitude", "latitude"], + "description": "Axis to flip around" + } + ], + "examples": [ + { + "name": "Mirror longitude", + "parameters": { "axis": "longitude" } + } + ] + }, + { + "id": "flip-vertical", + "name": "Flip Vertical", + "category": "transform", + "description": "Flip features vertically around the latitude axis", + "input_types": ["Feature", "FeatureCollection", "geometry"], + "output_types": ["Feature", "FeatureCollection", "geometry"], + "parameters": [], + "examples": [ + { + "name": "Mirror latitude", + "parameters": {} + } + ] + }, + { + "id": "speed-series", + "name": "Calculate Speed Series", + "category": "analysis", + "description": "Calculate speed time series from GPS track with timestamps", + "input_types": ["Feature"], + "output_types": ["array"], + "parameters": [ + { + "name": "time_unit", + "type": "string", + "default": "seconds", + "enum": ["seconds", "minutes", "hours"], + "description": "Time unit for speed calculation" + }, + { + "name": "smoothing", + "type": "boolean", + "default": false, + "description": "Apply smoothing to speed values" + }, + { + "name": "window_size", + "type": "number", + "default": 3, + "description": "Smoothing window size" + } + ], + "examples": [ + { + "name": "Speed in m/s", + "parameters": { "time_unit": "seconds" } + }, + { + "name": "Smoothed speed in km/h", + "parameters": { "time_unit": "hours", "smoothing": true, "window_size": 5 } + } + ] + }, + { + "id": "direction-series", + "name": "Calculate Direction Series", + "category": "analysis", + "description": "Calculate bearing/direction time series from GPS track", + "input_types": ["Feature"], + "output_types": ["array"], + "parameters": [ + { + "name": "smoothing", + "type": "boolean", + "default": false, + "description": "Apply smoothing to direction values" + }, + { + "name": "window_size", + "type": "number", + "default": 3, + "description": "Smoothing window size" + } + ], + "examples": [ + { + "name": "Raw bearings", + "parameters": { "smoothing": false } + }, + { + "name": "Smoothed bearings", + "parameters": { "smoothing": true, "window_size": 5 } + } + ] + }, + { + "id": "average-speed", + "name": "Calculate Average Speed", + "category": "statistics", + "description": "Calculate the average speed over an entire GPS track", + "input_types": ["Feature"], + "output_types": ["number"], + "parameters": [ + { + "name": "time_unit", + "type": "string", + "default": "seconds", + "enum": ["seconds", "minutes", "hours"], + "description": "Time unit for speed calculation" + } + ], + "examples": [ + { + "name": "Average speed m/s", + "parameters": { "time_unit": "seconds" } + }, + { + "name": "Average speed km/h", + "parameters": { "time_unit": "hours" } + } + ] + }, + { + "id": "speed-histogram", + "name": "Create Speed Histogram", + "category": "statistics", + "description": "Generate a histogram of speeds from GPS track data", + "input_types": ["Feature"], + "output_types": ["object"], + "parameters": [ + { + "name": "bins", + "type": "number", + "default": 10, + "description": "Number of histogram bins" + }, + { + "name": "time_unit", + "type": "string", + "default": "seconds", + "enum": ["seconds", "minutes", "hours"], + "description": "Time unit for speed calculation" + } + ], + "examples": [ + { + "name": "10-bin histogram", + "parameters": { "bins": 10, "time_unit": "seconds" } + }, + { + "name": "20-bin histogram in km/h", + "parameters": { "bins": 20, "time_unit": "hours" } + } + ] + }, + { + "id": "smooth-polyline", + "name": "Smooth Polyline", + "category": "processing", + "description": "Apply smoothing algorithms to LineString geometries", + "input_types": ["Feature", "FeatureCollection", "geometry"], + "output_types": ["Feature", "FeatureCollection", "geometry"], + "parameters": [ + { + "name": "algorithm", + "type": "string", + "default": "gaussian", + "enum": ["gaussian", "moving_average"], + "description": "Smoothing algorithm to use" + }, + { + "name": "window_size", + "type": "number", + "default": 3, + "description": "Smoothing window size" + } + ], + "examples": [ + { + "name": "Gaussian smoothing", + "parameters": { "algorithm": "gaussian", "window_size": 3 } + }, + { + "name": "Moving average", + "parameters": { "algorithm": "moving_average", "window_size": 5 } + } + ] + }, + { + "id": "export-csv", + "name": "Export to CSV", + "category": "io", + "description": "Export GeoJSON features to CSV format with coordinates and properties", + "input_types": ["Feature", "FeatureCollection", "geometry"], + "output_types": ["string"], + "parameters": [ + { + "name": "include_properties", + "type": "boolean", + "default": false, + "description": "Include feature properties in CSV output" + }, + { + "name": "coordinate_format", + "type": "string", + "default": "separate", + "enum": ["separate", "wkt"], + "description": "How to format coordinates" + }, + { + "name": "separator", + "type": "string", + "default": ",", + "description": "CSV field separator" + } + ], + "examples": [ + { + "name": "Basic CSV", + "parameters": { "include_properties": false } + }, + { + "name": "CSV with properties", + "parameters": { "include_properties": true, "separator": ";" } + } + ] + }, + { + "id": "export-rep", + "name": "Export to REP Format", + "category": "io", + "description": "Export GeoJSON features to Debrief REP format for vessel tracking", + "input_types": ["Feature", "FeatureCollection"], + "output_types": ["string"], + "parameters": [ + { + "name": "precision", + "type": "number", + "default": 6, + "description": "Coordinate precision (decimal places)" + } + ], + "examples": [ + { + "name": "Standard precision", + "parameters": { "precision": 6 } + }, + { + "name": "High precision", + "parameters": { "precision": 8 } + } + ] + }, + { + "id": "import-rep", + "name": "Import REP Format", + "category": "io", + "description": "Import Debrief REP format files into GeoJSON FeatureCollection", + "input_types": ["string"], + "output_types": ["FeatureCollection"], + "parameters": [ + { + "name": "encoding", + "type": "string", + "default": "utf-8", + "description": "Text encoding for REP file" + } + ], + "examples": [ + { + "name": "UTF-8 encoding", + "parameters": { "encoding": "utf-8" } + } + ] + } + ], + "dependencies": { + "runtime": "browser", + "javascript_version": "ES2020" + }, + "test_coverage": { + "statements": "90.05%", + "branches": "78.20%", + "functions": "85.71%", + "lines": "90.00%" + }, + "bundle_size": "~25KB", + "created": "2024-01-19", + "last_updated": "2024-01-19" +} \ No newline at end of file diff --git a/examples/javascript-bundle/package-lock.json b/examples/javascript-bundle/package-lock.json new file mode 100644 index 0000000..48973a9 --- /dev/null +++ b/examples/javascript-bundle/package-lock.json @@ -0,0 +1,5312 @@ +{ + "name": "toolvault-javascript-bundle", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "toolvault-javascript-bundle", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "eslint": "^8.57.0", + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/node": { + "version": "24.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz", + "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "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/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "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/electron-to-chromium": { + "version": "1.5.207", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.207.tgz", + "integrity": "sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "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-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "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/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-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-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "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/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/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nwsapi": { + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, + "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/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "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/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/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/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.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/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/examples/javascript-bundle/package.json b/examples/javascript-bundle/package.json new file mode 100644 index 0000000..eef72f9 --- /dev/null +++ b/examples/javascript-bundle/package.json @@ -0,0 +1,36 @@ +{ + "name": "toolvault-javascript-bundle", + "version": "1.0.0", + "description": "Mock JavaScript tool bundle for ToolVault UI validation", + "main": "index.js", + "scripts": { + "test": "jest", + "test:watch": "jest --watch", + "test:coverage": "jest --coverage", + "lint": "eslint tools/ tests/", + "validate": "node scripts/validate-bundle.js" + }, + "keywords": ["toolvault", "gis", "analysis", "mock"], + "author": "ToolVault Project", + "license": "MIT", + "devDependencies": { + "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", + "eslint": "^8.57.0" + }, + "jest": { + "testEnvironment": "jsdom", + "collectCoverageFrom": [ + "tools/**/*.js" + ], + "coverageThreshold": { + "global": { + "statements": 100, + "branches": 95, + "functions": 100, + "lines": 100 + } + }, + "setupFilesAfterEnv": ["/tests/setup.js"] + } +} \ No newline at end of file diff --git a/examples/javascript-bundle/tests/analysis/direction-series.test.js b/examples/javascript-bundle/tests/analysis/direction-series.test.js new file mode 100644 index 0000000..bc665e7 --- /dev/null +++ b/examples/javascript-bundle/tests/analysis/direction-series.test.js @@ -0,0 +1,149 @@ +// Load tool +require('../../tools/analysis/direction-series.js'); +const { sampleTrackWithTimestamps } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Calculate Direction Series', () => { + test('should calculate direction series from GPS track', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.calculateDirectionSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + expect(result[0]).toHaveProperty('time'); + expect(result[0]).toHaveProperty('direction'); + expect(typeof result[0].direction).toBe('number'); + expect(result[0].direction).toBeGreaterThanOrEqual(0); + expect(result[0].direction).toBeLessThan(360); + }); + + test('should apply smoothing when requested', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const rawResult = window.ToolVault.tools.calculateDirectionSeries(input, { smoothing: false }); + const smoothedResult = window.ToolVault.tools.calculateDirectionSeries(input, { + smoothing: true, + window_size: 3 + }); + + expect(rawResult.length).toBe(smoothedResult.length); + expect(smoothedResult[0]).toHaveProperty('time'); + expect(smoothedResult[0]).toHaveProperty('direction'); + }); + + test('should handle direct LineString geometry', () => { + const input = { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ], + properties: { + timestamps: [1642505200000, 1642505260000] + } + }; + const result = window.ToolVault.tools.calculateDirectionSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + }); + + test('should use default parameters when none provided', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.calculateDirectionSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + }); + + test('should handle track with insufficient coordinates', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000] + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074] + ] + } + }; + const result = window.ToolVault.tools.calculateDirectionSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(0); + }); + + test('should handle track with no timestamps', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ] + } + }; + const result = window.ToolVault.tools.calculateDirectionSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(0); + }); + + test('should calculate correct bearings for cardinal directions', () => { + // North movement + const northInput = { + type: 'Feature', + properties: { + timestamps: [1642505200000, 1642505260000] + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [0, 1] // Move north + ] + } + }; + const northResult = window.ToolVault.tools.calculateDirectionSeries(northInput); + expect(northResult[0].direction).toBeCloseTo(0, 1); // Should be close to 0° (north) + + // East movement + const eastInput = { + type: 'Feature', + properties: { + timestamps: [1642505200000, 1642505260000] + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [1, 0] // Move east + ] + } + }; + const eastResult = window.ToolVault.tools.calculateDirectionSeries(eastInput); + expect(eastResult[0].direction).toBeCloseTo(90, 1); // Should be close to 90° (east) + }); + + test('should handle smoothing with different window sizes', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + + const smallWindow = window.ToolVault.tools.calculateDirectionSeries(input, { + smoothing: true, + window_size: 1 + }); + const largeWindow = window.ToolVault.tools.calculateDirectionSeries(input, { + smoothing: true, + window_size: 5 + }); + + expect(smallWindow.length).toBe(largeWindow.length); + expect(smallWindow[0]).toHaveProperty('direction'); + expect(largeWindow[0]).toHaveProperty('direction'); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/analysis/speed-series.test.js b/examples/javascript-bundle/tests/analysis/speed-series.test.js new file mode 100644 index 0000000..1c6d80b --- /dev/null +++ b/examples/javascript-bundle/tests/analysis/speed-series.test.js @@ -0,0 +1,117 @@ +// Load tool +require('../../tools/analysis/speed-series.js'); +const { sampleTrackWithTimestamps } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Calculate Speed Series', () => { + test('should calculate speed series from GPS track', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const params = { time_unit: 'seconds' }; + const result = window.ToolVault.tools.calculateSpeedSeries(input, params); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + expect(result[0]).toHaveProperty('time'); + expect(result[0]).toHaveProperty('speed'); + expect(typeof result[0].speed).toBe('number'); + expect(result[0].speed).toBeGreaterThan(0); + }); + + test('should handle different time units', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + + const secondsResult = window.ToolVault.tools.calculateSpeedSeries(input, { time_unit: 'seconds' }); + const minutesResult = window.ToolVault.tools.calculateSpeedSeries(input, { time_unit: 'minutes' }); + const hoursResult = window.ToolVault.tools.calculateSpeedSeries(input, { time_unit: 'hours' }); + + expect(secondsResult[0].speed).toBeGreaterThan(minutesResult[0].speed); + expect(minutesResult[0].speed).toBeGreaterThan(hoursResult[0].speed); + }); + + test('should handle direct LineString geometry', () => { + const input = { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ], + properties: { + timestamps: [1642505200000, 1642505260000] // 60 second interval + } + }; + const result = window.ToolVault.tools.calculateSpeedSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + }); + + test('should use default parameters when none provided', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.calculateSpeedSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBeGreaterThan(0); + expect(typeof result[0].speed).toBe('number'); + }); + + test('should handle track with insufficient timestamps', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000] // Only one timestamp + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ] + } + }; + const result = window.ToolVault.tools.calculateSpeedSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(0); // No speed calculations possible + }); + + test('should handle track with no timestamps', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ] + } + }; + const result = window.ToolVault.tools.calculateSpeedSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(0); + }); + + test('should skip zero time intervals', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000, 1642505200000, 1642505260000] // Same timestamp twice + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076], + [-0.1280, 51.5078] + ] + } + }; + const result = window.ToolVault.tools.calculateSpeedSeries(input); + + expect(Array.isArray(result)).toBe(true); + expect(result.length).toBe(1); // Only one valid speed calculation + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/helpers.js b/examples/javascript-bundle/tests/helpers.js new file mode 100644 index 0000000..011682f --- /dev/null +++ b/examples/javascript-bundle/tests/helpers.js @@ -0,0 +1,102 @@ +// Test helper functions and sample data for ToolVault JavaScript Bundle + +// Sample GeoJSON Feature for Point testing +const samplePoint = { + type: 'Feature', + properties: { + name: 'Test Point', + timestamp: 1640995200000 // 2022-01-01T00:00:00Z + }, + geometry: { + type: 'Point', + coordinates: [-0.1276, 51.5074] // London coordinates + } +}; + +// Sample LineString for track testing +const sampleLineString = { + type: 'Feature', + properties: { + name: 'Test Line', + timestamps: [ + 1640995200000, // 2022-01-01T00:00:00Z + 1640995260000, // 2022-01-01T00:01:00Z + 1640995320000 // 2022-01-01T00:02:00Z + ] + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076], + [-0.1280, 51.5078] + ] + } +}; + +// Sample FeatureCollection for testing multiple features +const sampleFeatureCollection = { + type: 'FeatureCollection', + features: [ + samplePoint, + sampleLineString + ] +}; + +// GPS track with timestamps for speed/direction analysis +const sampleTrackWithTimestamps = { + type: 'Feature', + properties: { + name: 'GPS Track', + timestamps: [ + 1642505200000, // 2022-01-18T10:00:00Z + 1642505205000, // 2022-01-18T10:00:05Z (5 second intervals) + 1642505210000 // 2022-01-18T10:00:10Z + ] + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076], + [-0.1280, 51.5078] + ] + } +}; + +// Helper function to create realistic GPS tracks for performance testing +function createLargeTrack(numPoints = 100) { + const coordinates = []; + const timestamps = []; + const startTime = 1642505200000; + const startLat = 51.5074; + const startLon = -0.1276; + + for (let i = 0; i < numPoints; i++) { + // Small random movements to simulate GPS track + const lat = startLat + (Math.random() - 0.5) * 0.01; + const lon = startLon + (Math.random() - 0.5) * 0.01; + coordinates.push([lon, lat]); + timestamps.push(startTime + i * 60000); // 1 minute intervals + } + + return { + type: 'Feature', + properties: { + name: 'Large Track', + timestamps: timestamps + }, + geometry: { + type: 'LineString', + coordinates: coordinates + } + }; +} + +module.exports = { + samplePoint, + sampleLineString, + sampleFeatureCollection, + sampleTrackWithTimestamps, + createLargeTrack +}; \ No newline at end of file diff --git a/examples/javascript-bundle/tests/integration/tool-loading.test.js b/examples/javascript-bundle/tests/integration/tool-loading.test.js new file mode 100644 index 0000000..317bdd9 --- /dev/null +++ b/examples/javascript-bundle/tests/integration/tool-loading.test.js @@ -0,0 +1,244 @@ +// Integration tests for tool loading and basic functionality +require('../../tools/transform/translate.js'); +require('../../tools/transform/flip-horizontal.js'); +require('../../tools/transform/flip-vertical.js'); +require('../../tools/analysis/speed-series.js'); +require('../../tools/analysis/direction-series.js'); +require('../../tools/statistics/average-speed.js'); +require('../../tools/statistics/speed-histogram.js'); +require('../../tools/processing/smooth-polyline.js'); +require('../../tools/io/export-csv.js'); +require('../../tools/io/export-rep.js'); +require('../../tools/io/import-rep.js'); + +// Initialize window object +global.window = global.window || {}; + +describe('Tool Loading Integration', () => { + test('should load all tools without conflicts', () => { + expect(window.ToolVault).toBeDefined(); + expect(window.ToolVault.tools).toBeDefined(); + + // Transform tools + expect(typeof window.ToolVault.tools.translate).toBe('function'); + expect(typeof window.ToolVault.tools.flipHorizontal).toBe('function'); + expect(typeof window.ToolVault.tools.flipVertical).toBe('function'); + + // Analysis tools + expect(typeof window.ToolVault.tools.calculateSpeedSeries).toBe('function'); + expect(typeof window.ToolVault.tools.calculateDirectionSeries).toBe('function'); + + // Statistics tools + expect(typeof window.ToolVault.tools.calculateAverageSpeed).toBe('function'); + expect(typeof window.ToolVault.tools.createSpeedHistogram).toBe('function'); + + // Processing tools + expect(typeof window.ToolVault.tools.smoothPolyline).toBe('function'); + + // I/O tools + expect(typeof window.ToolVault.tools.exportCSV).toBe('function'); + expect(typeof window.ToolVault.tools.exportREP).toBe('function'); + expect(typeof window.ToolVault.tools.importREP).toBe('function'); + }); + + test('should have consistent tool naming conventions', () => { + const toolNames = Object.keys(window.ToolVault.tools); + + // All tool names should be camelCase + toolNames.forEach(name => { + expect(name).toMatch(/^[a-z][a-zA-Z0-9]*$/); + }); + + // Verify expected tool count + expect(toolNames.length).toBe(11); + }); + + test('should support tool chaining workflow', () => { + // Create a sample track + const track = { + type: 'Feature', + properties: { + timestamps: [0, 60000, 120000] // 1 minute intervals + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [0.001, 0.001], + [0.002, 0.002] + ] + } + }; + + // 1. Calculate speed series + const speedSeries = window.ToolVault.tools.calculateSpeedSeries(track, { time_unit: 'seconds' }); + expect(Array.isArray(speedSeries)).toBe(true); + expect(speedSeries.length).toBeGreaterThan(0); + + // 2. Calculate average speed + const avgSpeed = window.ToolVault.tools.calculateAverageSpeed(track, { time_unit: 'seconds' }); + expect(typeof avgSpeed).toBe('number'); + expect(avgSpeed).toBeGreaterThan(0); + + // 3. Create histogram from the track + const histogram = window.ToolVault.tools.createSpeedHistogram(track, { bins: 5 }); + expect(histogram).toHaveProperty('bins'); + expect(histogram).toHaveProperty('counts'); + + // 4. Smooth the track + const smoothed = window.ToolVault.tools.smoothPolyline(track); + expect(smoothed.geometry.type).toBe('LineString'); + + // 5. Export to CSV + const csv = window.ToolVault.tools.exportCSV(smoothed); + expect(typeof csv).toBe('string'); + expect(csv).toContain('longitude,latitude'); + }); + + test('should handle REP format roundtrip', () => { + // Create REP data + const repData = '220118 120000.000 TEST_VESSEL @ 50 30 0.000 N 1 15 0.000 W 90 15 100'; + + // Import REP data + const imported = window.ToolVault.tools.importREP(repData); + expect(imported.type).toBe('FeatureCollection'); + expect(imported.features).toHaveLength(1); + + // Export back to REP + const exported = window.ToolVault.tools.exportREP(imported.features[0]); + expect(typeof exported).toBe('string'); + expect(exported).toContain('TEST_VESSEL'); + expect(exported).toContain('220118'); + }); + + test('should maintain data integrity through transformations', () => { + const originalTrack = { + type: 'Feature', + properties: { + name: 'Test Track', + color: 'blue' + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [1, 1], + [2, 2] + ] + } + }; + + // Apply translate transformation + const translated = window.ToolVault.tools.translate(originalTrack, { dx: 1, dy: 1 }); + + // Properties should be preserved + expect(translated.properties.name).toBe('Test Track'); + expect(translated.properties.color).toBe('blue'); + + // Geometry structure should be maintained + expect(translated.geometry.type).toBe('LineString'); + expect(translated.geometry.coordinates).toHaveLength(3); + + // Coordinates should be transformed correctly + expect(translated.geometry.coordinates[0]).toEqual([1, 1]); + expect(translated.geometry.coordinates[1]).toEqual([2, 2]); + expect(translated.geometry.coordinates[2]).toEqual([3, 3]); + }); + + test('should handle error conditions gracefully', () => { + // Test with invalid input types + expect(() => { + window.ToolVault.tools.translate(null); + }).not.toThrow(); + + expect(() => { + window.ToolVault.tools.calculateSpeedSeries({}); + }).not.toThrow(); + + expect(() => { + window.ToolVault.tools.importREP('invalid rep format'); + }).not.toThrow(); + }); + + test('should support different input formats consistently', () => { + const pointFeature = { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + + const directGeometry = { + type: 'Point', + coordinates: [0, 0] + }; + + const featureCollection = { + type: 'FeatureCollection', + features: [pointFeature] + }; + + // All tools should handle these different input formats + const translateResult1 = window.ToolVault.tools.translate(pointFeature, { dx: 1, dy: 1 }); + const translateResult2 = window.ToolVault.tools.translate(directGeometry, { dx: 1, dy: 1 }); + const translateResult3 = window.ToolVault.tools.translate(featureCollection, { dx: 1, dy: 1 }); + + expect(translateResult1).toBeDefined(); + expect(translateResult2).toBeDefined(); + expect(translateResult3).toBeDefined(); + + // CSV export should handle all formats + const csvResult1 = window.ToolVault.tools.exportCSV(pointFeature); + const csvResult2 = window.ToolVault.tools.exportCSV(directGeometry); + const csvResult3 = window.ToolVault.tools.exportCSV(featureCollection); + + expect(typeof csvResult1).toBe('string'); + expect(typeof csvResult2).toBe('string'); + expect(typeof csvResult3).toBe('string'); + }); + + test('should have tools with consistent parameter handling', () => { + const sampleTrack = { + type: 'Feature', + properties: { + timestamps: [0, 60000, 120000] + }, + geometry: { + type: 'LineString', + coordinates: [[0, 0], [0.001, 0.001], [0.002, 0.002]] + } + }; + + // All tools should work with no parameters (use defaults) + expect(() => window.ToolVault.tools.translate(sampleTrack)).not.toThrow(); + expect(() => window.ToolVault.tools.flipHorizontal(sampleTrack)).not.toThrow(); + expect(() => window.ToolVault.tools.calculateSpeedSeries(sampleTrack)).not.toThrow(); + expect(() => window.ToolVault.tools.calculateAverageSpeed(sampleTrack)).not.toThrow(); + expect(() => window.ToolVault.tools.smoothPolyline(sampleTrack)).not.toThrow(); + expect(() => window.ToolVault.tools.exportCSV(sampleTrack)).not.toThrow(); + + // All tools should work with empty parameter object + expect(() => window.ToolVault.tools.translate(sampleTrack, {})).not.toThrow(); + expect(() => window.ToolVault.tools.calculateSpeedSeries(sampleTrack, {})).not.toThrow(); + expect(() => window.ToolVault.tools.createSpeedHistogram(sampleTrack, {})).not.toThrow(); + }); + + test('should preserve tool independence', () => { + // Modifying one tool's namespace shouldn't affect others + const originalTranslate = window.ToolVault.tools.translate; + + // Temporarily modify translate tool + window.ToolVault.tools.translate = null; + + // Other tools should still work + expect(typeof window.ToolVault.tools.flipHorizontal).toBe('function'); + expect(typeof window.ToolVault.tools.calculateSpeedSeries).toBe('function'); + expect(typeof window.ToolVault.tools.exportCSV).toBe('function'); + + // Restore original tool + window.ToolVault.tools.translate = originalTranslate; + expect(typeof window.ToolVault.tools.translate).toBe('function'); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/io/export-csv.test.js b/examples/javascript-bundle/tests/io/export-csv.test.js new file mode 100644 index 0000000..83829ad --- /dev/null +++ b/examples/javascript-bundle/tests/io/export-csv.test.js @@ -0,0 +1,230 @@ +// Load tool +require('../../tools/io/export-csv.js'); +const { samplePoint, sampleLineString, sampleTrackWithTimestamps } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Export to CSV', () => { + test('should export Point feature to CSV format', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const result = window.ToolVault.tools.exportCSV(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('longitude,latitude'); + expect(result).toContain('-0.1276,51.5074'); + }); + + test('should export LineString feature to CSV format', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + const result = window.ToolVault.tools.exportCSV(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('longitude,latitude'); + + // Should contain multiple rows for LineString coordinates + const lines = result.split('\n').filter(line => line.trim()); + expect(lines.length).toBeGreaterThan(1); // Header + data rows + expect(lines[0]).toBe('longitude,latitude,timestamp'); + }); + + test('should include timestamps when available', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.exportCSV(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('longitude,latitude,timestamp'); + + // Should contain timestamp values + expect(result).toContain('1642505200000'); + expect(result).toContain('1642505205000'); + expect(result).toContain('1642505210000'); + }); + + test('should handle FeatureCollection input', () => { + const input = { + type: 'FeatureCollection', + features: [ + JSON.parse(JSON.stringify(samplePoint)), + JSON.parse(JSON.stringify(sampleLineString)) + ] + }; + const result = window.ToolVault.tools.exportCSV(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('longitude,latitude'); + + // Should contain data from multiple features + const lines = result.split('\n').filter(line => line.trim()); + expect(lines.length).toBeGreaterThan(4); // Header + multiple data rows + }); + + test('should handle direct geometry input', () => { + const input = { + type: 'Point', + coordinates: [-1, 52] + }; + const result = window.ToolVault.tools.exportCSV(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('longitude,latitude'); + expect(result).toContain('-1,52'); + }); + + test('should include properties when include_properties is true', () => { + const input = { + type: 'Feature', + properties: { + name: 'Test Point', + speed: 10.5, + heading: 45 + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportCSV(input, { include_properties: true }); + + expect(result).toContain('longitude,latitude,name,speed,heading'); + expect(result).toContain('0,0,Test Point,10.5,45'); + }); + + test('should exclude properties when include_properties is false', () => { + const input = { + type: 'Feature', + properties: { + name: 'Test Point', + speed: 10.5 + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportCSV(input, { include_properties: false }); + + expect(result).toContain('longitude,latitude'); + expect(result).not.toContain('name'); + expect(result).not.toContain('speed'); + expect(result).toContain('0,0'); + }); + + test('should use default parameters when none provided', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const result = window.ToolVault.tools.exportCSV(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('longitude,latitude'); + }); + + test('should handle custom separator', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const result = window.ToolVault.tools.exportCSV(input, { separator: ';' }); + + expect(result).toContain('longitude;latitude'); + expect(result).toContain('-0.1276;51.5074'); + }); + + test('should handle empty FeatureCollection', () => { + const input = { + type: 'FeatureCollection', + features: [] + }; + const result = window.ToolVault.tools.exportCSV(input); + + expect(typeof result).toBe('string'); + expect(result).toBe('longitude,latitude\n'); + }); + + test('should handle LineString with timestamps', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1000, 2000, 3000] + }, + geometry: { + type: 'LineString', + coordinates: [[0, 0], [1, 1], [2, 2]] + } + }; + const result = window.ToolVault.tools.exportCSV(input); + + expect(result).toContain('longitude,latitude,timestamp'); + expect(result).toContain('0,0,1000'); + expect(result).toContain('1,1,2000'); + expect(result).toContain('2,2,3000'); + }); + + test('should handle mismatched timestamps and coordinates', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1000, 2000] // Only 2 timestamps + }, + geometry: { + type: 'LineString', + coordinates: [[0, 0], [1, 1], [2, 2]] // 3 coordinates + } + }; + const result = window.ToolVault.tools.exportCSV(input); + + expect(result).toContain('longitude,latitude,timestamp'); + expect(result).toContain('0,0,1000'); + expect(result).toContain('1,1,2000'); + expect(result).toContain('2,2,'); // No timestamp for third coordinate + }); + + test('should handle Polygon geometry', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'Polygon', + coordinates: [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]] + } + }; + const result = window.ToolVault.tools.exportCSV(input); + + expect(result).toContain('longitude,latitude'); + const lines = result.split('\n').filter(line => line.trim()); + expect(lines.length).toBe(6); // Header + 5 coordinates + }); + + test('should escape CSV special characters in properties', () => { + const input = { + type: 'Feature', + properties: { + name: 'Point, with comma', + description: 'Has "quotes" in it' + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportCSV(input, { include_properties: true }); + + expect(result).toContain('"Point, with comma"'); + expect(result).toContain('"Has ""quotes"" in it"'); + }); + + test('should handle null and undefined property values', () => { + const input = { + type: 'Feature', + properties: { + name: null, + speed: undefined, + heading: 0 + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportCSV(input, { include_properties: true }); + + expect(result).toContain('longitude,latitude,name,speed,heading'); + expect(result).toContain('0,0,,,0'); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/io/export-rep.test.js b/examples/javascript-bundle/tests/io/export-rep.test.js new file mode 100644 index 0000000..5b3ba66 --- /dev/null +++ b/examples/javascript-bundle/tests/io/export-rep.test.js @@ -0,0 +1,328 @@ +// Load tool +require('../../tools/io/export-rep.js'); +const { samplePoint, sampleLineString, sampleTrackWithTimestamps } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Export to REP Format', () => { + test('should export Point feature to REP format', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'TEST_VESSEL' + }, + geometry: { + type: 'Point', + coordinates: [-0.1276, 51.5074] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('TEST_VESSEL'); + expect(result).toContain('220118'); // Date: 18 Jan 2022 + expect(result).toContain('112640.000'); // Time: 11:26:40.000 + + // Should contain DMS coordinates + expect(result).toMatch(/\d+ \d+ [\d.]+ [NSEW]/); // DMS format + }); + + test('should export LineString feature to REP format', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + input.properties.vessel_name = 'TRACK_VESSEL'; + + const result = window.ToolVault.tools.exportREP(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('TRACK_VESSEL'); + + // Should have multiple lines for LineString coordinates + const lines = result.split('\n').filter(line => line.trim()); + expect(lines.length).toBeGreaterThan(1); + + // Each line should follow REP format + lines.forEach(line => { + if (line.trim()) { + expect(line).toMatch(/^\d{6} \d{6}\.\d{3} .+ @ \d+ \d+ [\d.]+ [NS] \d+ \d+ [\d.]+ [EW] \d+ \d+ \d+$/); + } + }); + }); + + test('should handle FeatureCollection input', () => { + const input = { + type: 'FeatureCollection', + features: [ + { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'VESSEL_1' + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }, + { + type: 'Feature', + properties: { + timestamps: [1642505260000], + vessel_name: 'VESSEL_2' + }, + geometry: { + type: 'Point', + coordinates: [1, 1] + } + } + ] + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toContain('VESSEL_1'); + expect(result).toContain('VESSEL_2'); + + const lines = result.split('\n').filter(line => line.trim()); + expect(lines.length).toBe(2); + }); + + test('should handle direct geometry input', () => { + const input = { + type: 'Point', + coordinates: [0, 0], + properties: { + timestamps: [1642505200000] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('UNKNOWN'); // Default vessel name + expect(result).toContain('700101'); // Default date when no timestamp provided + }); + + test('should use default vessel name when not provided', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000] + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toContain('UNKNOWN'); + }); + + test('should handle timestamps in different formats', () => { + const input = { + type: 'Feature', + properties: { + timestamps: ['2022-01-18T12:00:00.000Z'], + vessel_name: 'TEST_VESSEL' + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toContain('220118'); + expect(result).toContain('120000.000'); + }); + + test('should handle negative coordinates correctly', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'SOUTH_WEST' + }, + geometry: { + type: 'Point', + coordinates: [-1.5, -2.5] // West longitude, South latitude + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toContain('S'); // South hemisphere + expect(result).toContain('W'); // West hemisphere + + // Should contain positive DMS values (hemisphere indicator handles sign) + expect(result).toMatch(/\d+ \d+ [\d.]+ S/); + expect(result).toMatch(/\d+ \d+ [\d.]+ W/); + }); + + test('should include heading, speed, and depth when provided', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'VESSEL_WITH_DATA', + heading: [45], + speed: [10.5], + depth: [100] + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toContain('45'); // Heading + expect(result).toContain('10.5'); // Speed + expect(result).toContain('100'); // Depth + }); + + test('should use default values for missing heading, speed, depth', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'MINIMAL_VESSEL' + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toMatch(/0 0 0$/); // Default heading, speed, depth + }); + + test('should handle empty timestamps gracefully', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [], + vessel_name: 'NO_TIME_VESSEL' + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toBe(''); // No output for no timestamps + }); + + test('should handle coordinates without timestamps', () => { + const input = { + type: 'Feature', + properties: { + vessel_name: 'NO_TIMESTAMPS' + }, + geometry: { + type: 'LineString', + coordinates: [[0, 0], [1, 1]] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toBe(''); // No output without timestamps + }); + + test('should convert DMS coordinates correctly', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'DMS_TEST' + }, + geometry: { + type: 'Point', + coordinates: [1.5, 2.5] // 1°30'0" E, 2°30'0" N + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toContain('2 30 0 N'); // Latitude: 2°30'0" N + expect(result).toContain('1 30 0 E'); // Longitude: 1°30'0" E + }); + + test('should handle Polygon geometry', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1000, 2000, 3000, 4000, 5000], + vessel_name: 'POLYGON_VESSEL' + }, + geometry: { + type: 'Polygon', + coordinates: [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + const lines = result.split('\n').filter(line => line.trim()); + expect(lines.length).toBe(5); // 5 coordinates with timestamps + + lines.forEach(line => { + expect(line).toContain('POLYGON_VESSEL'); + }); + }); + + test('should handle mismatched timestamps and coordinates', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1000, 2000], // Only 2 timestamps + vessel_name: 'MISMATCH_VESSEL' + }, + geometry: { + type: 'LineString', + coordinates: [[0, 0], [1, 1], [2, 2]] // 3 coordinates + } + }; + const result = window.ToolVault.tools.exportREP(input); + + const lines = result.split('\n').filter(line => line.trim()); + expect(lines.length).toBe(2); // Only 2 lines for 2 timestamps + }); + + test('should format vessel names with special characters', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'VESSEL-WITH_SPECIAL.CHARS' + }, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(result).toContain('VESSEL-WITH_SPECIAL.CHARS'); + }); + + test('should handle very precise coordinates', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000], + vessel_name: 'PRECISE_VESSEL' + }, + geometry: { + type: 'Point', + coordinates: [1.123456789, 2.987654321] + } + }; + const result = window.ToolVault.tools.exportREP(input); + + expect(typeof result).toBe('string'); + expect(result).toContain('PRECISE_VESSEL'); + // Should handle precision in DMS conversion + expect(result).toMatch(/\d+ \d+ [\d.]+ [NS]/); + expect(result).toMatch(/\d+ \d+ [\d.]+ [EW]/); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/io/import-rep.test.js b/examples/javascript-bundle/tests/io/import-rep.test.js new file mode 100644 index 0000000..bdb2a75 --- /dev/null +++ b/examples/javascript-bundle/tests/io/import-rep.test.js @@ -0,0 +1,230 @@ +// Load tool +require('../../tools/io/import-rep.js'); + +// Initialize window object +global.window = global.window || {}; + +describe('Import REP Format', () => { + test('should import single track point from REP format', () => { + const repData = '220118 120000.000 NELSON @ 50 54 0.000 N 1 24 0.000 W 45 10 100'; + const result = window.ToolVault.tools.importREP(repData); + + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(1); + + const track = result.features[0]; + expect(track.type).toBe('Feature'); + expect(track.properties.vessel_name).toBe('NELSON'); + expect(track.geometry.type).toBe('LineString'); + expect(track.geometry.coordinates).toHaveLength(1); + + // Should convert DMS to decimal degrees + expect(track.geometry.coordinates[0][0]).toBeCloseTo(-1.4, 1); // 1°24'0" W + expect(track.geometry.coordinates[0][1]).toBeCloseTo(50.9, 1); // 50°54'0" N + + // Should have timestamp + expect(track.properties.timestamps).toHaveLength(1); + expect(track.properties.timestamps[0]).toBe(1642507200000); // 2022-01-18 12:00:00 UTC + }); + + test('should import multiple track points for single vessel', () => { + const repData = `220118 120000.000 NELSON @ 50 54 0.000 N 1 24 0.000 W 45 10 100 +220118 120100.000 NELSON @ 50 55 0.000 N 1 25 0.000 W 90 15 105`; + + const result = window.ToolVault.tools.importREP(repData); + + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(1); // Single vessel + + const track = result.features[0]; + expect(track.properties.vessel_name).toBe('NELSON'); + expect(track.geometry.coordinates).toHaveLength(2); + expect(track.properties.timestamps).toHaveLength(2); + + // Verify second point + expect(track.geometry.coordinates[1][1]).toBeCloseTo(50.916667, 4); // 50°55'0" N + }); + + test('should import multiple vessels as separate features', () => { + const repData = `220118 120000.000 NELSON @ 50 54 0.000 N 1 24 0.000 W 45 10 100 +220118 120000.000 CARRIER @ 51 30 0.000 N 0 10 0.000 W 180 20 50`; + + const result = window.ToolVault.tools.importREP(repData); + + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(2); + + const vesselNames = result.features.map(f => f.properties.vessel_name); + expect(vesselNames).toContain('NELSON'); + expect(vesselNames).toContain('CARRIER'); + }); + + test('should handle quoted vessel names', () => { + const repData = '220118 120000.000 "HMS Nelson" @ 50 54 0.000 N 1 24 0.000 W 45 10 100'; + const result = window.ToolVault.tools.importREP(repData); + + expect(result.features[0].properties.vessel_name).toBe('HMS Nelson'); + }); + + test('should ignore comment lines starting with semicolon', () => { + const repData = `; This is a comment +220118 120000.000 NELSON @ 50 54 0.000 N 1 24 0.000 W 45 10 100 +; Another comment +220118 120100.000 NELSON @ 50 55 0.000 N 1 25 0.000 W 90 15 105`; + + const result = window.ToolVault.tools.importREP(repData); + + expect(result.features).toHaveLength(1); + expect(result.features[0].geometry.coordinates).toHaveLength(2); + }); + + test('should handle empty lines and whitespace', () => { + const repData = ` +220118 120000.000 NELSON @ 50 54 0.000 N 1 24 0.000 W 45 10 100 + +220118 120100.000 NELSON @ 50 55 0.000 N 1 25 0.000 W 90 15 105 + `; + + const result = window.ToolVault.tools.importREP(repData); + + expect(result.features).toHaveLength(1); + expect(result.features[0].geometry.coordinates).toHaveLength(2); + }); + + test('should handle southern and western hemispheres', () => { + const repData = '220118 120000.000 SOUTHERN @ 30 30 30.000 S 45 45 45.000 W 0 0 0'; + const result = window.ToolVault.tools.importREP(repData); + + const coords = result.features[0].geometry.coordinates[0]; + expect(coords[0]).toBeLessThan(0); // Western longitude should be negative + expect(coords[1]).toBeLessThan(0); // Southern latitude should be negative + + // Should be approximately -45.7625° (45°45'45" W) and -30.5083° (30°30'30" S) + expect(coords[0]).toBeCloseTo(-45.7625, 3); + expect(coords[1]).toBeCloseTo(-30.5083, 3); + }); + + test('should handle missing optional parameters', () => { + const repData = '220118 120000.000 BASIC @ 50 0 0.000 N 0 0 0.000 E'; + const result = window.ToolVault.tools.importREP(repData); + + const track = result.features[0]; + expect(track.properties.vessel_name).toBe('BASIC'); + expect(track.geometry.coordinates).toHaveLength(1); + expect(track.geometry.coordinates[0]).toEqual([0, 50]); + }); + + test('should handle malformed lines gracefully', () => { + const repData = `220118 120000.000 GOOD @ 50 0 0.000 N 0 0 0.000 E 0 0 0 +invalid line format +220118 120100.000 GOOD @ 51 0 0.000 N 0 0 0.000 E 0 0 0`; + + const result = window.ToolVault.tools.importREP(repData); + + expect(result.features).toHaveLength(1); + expect(result.features[0].geometry.coordinates).toHaveLength(2); // Should skip invalid line + }); + + test('should store heading, speed, and depth when provided', () => { + const repData = '220118 120000.000 VESSEL @ 50 0 0.000 N 0 0 0.000 E 90 25 150'; + const result = window.ToolVault.tools.importREP(repData); + + const track = result.features[0]; + expect(track.properties).toHaveProperty('heading'); + expect(track.properties).toHaveProperty('speed'); + expect(track.properties).toHaveProperty('depth'); + + expect(track.properties.heading[0]).toBe(90); + expect(track.properties.speed[0]).toBe(25); + expect(track.properties.depth[0]).toBe(150); + }); + + test('should handle fractional seconds in timestamps', () => { + const repData = '220118 120030.500 VESSEL @ 50 0 0.000 N 0 0 0.000 E 0 0 0'; + const result = window.ToolVault.tools.importREP(repData); + + const timestamp = result.features[0].properties.timestamps[0]; + // Should be 30.5 seconds after 12:00:00 on 2022-01-18 (12:00:30.500 UTC) + expect(timestamp).toBe(1642507230500); + }); + + test('should handle vessel name with spaces and special characters', () => { + const repData = '220118 120000.000 "HMS Victory-2" @ 50 0 0.000 N 0 0 0.000 E 0 0 0'; + const result = window.ToolVault.tools.importREP(repData); + + expect(result.features[0].properties.vessel_name).toBe('HMS Victory-2'); + }); + + test('should handle different date formats correctly', () => { + const repData = '211225 235959.999 NEWYEAR @ 0 0 0.000 N 0 0 0.000 E 0 0 0'; + const result = window.ToolVault.tools.importREP(repData); + + // Should parse as December 25, 2021, 23:59:59.999 + const timestamp = result.features[0].properties.timestamps[0]; + const date = new Date(timestamp); + expect(date.getUTCFullYear()).toBe(2021); + expect(date.getUTCMonth()).toBe(11); // December (0-indexed) + expect(date.getUTCDate()).toBe(25); + }); + + test('should handle empty input', () => { + const result = window.ToolVault.tools.importREP(''); + + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(0); + }); + + test('should handle input with only comments', () => { + const repData = `; Comment 1 +; Comment 2 +; Comment 3`; + + const result = window.ToolVault.tools.importREP(repData); + + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(0); + }); + + test('should handle precise DMS coordinates', () => { + const repData = '220118 120000.000 PRECISE @ 50 30 45.123 N 1 15 30.456 W 0 0 0'; + const result = window.ToolVault.tools.importREP(repData); + + const coords = result.features[0].geometry.coordinates[0]; + + // 1°15'30.456" W = -1.2584600° + expect(coords[0]).toBeCloseTo(-1.2584600, 6); + // 50°30'45.123" N = 50.5125342° + expect(coords[1]).toBeCloseTo(50.5125342, 6); + }); + + test('should create proper GeoJSON structure', () => { + const repData = '220118 120000.000 TEST @ 50 0 0.000 N 0 0 0.000 E 0 0 0'; + const result = window.ToolVault.tools.importREP(repData); + + // Verify GeoJSON structure + expect(result).toHaveProperty('type', 'FeatureCollection'); + expect(result).toHaveProperty('features'); + expect(Array.isArray(result.features)).toBe(true); + + const feature = result.features[0]; + expect(feature).toHaveProperty('type', 'Feature'); + expect(feature).toHaveProperty('properties'); + expect(feature).toHaveProperty('geometry'); + expect(feature.geometry).toHaveProperty('type', 'LineString'); + expect(feature.geometry).toHaveProperty('coordinates'); + expect(Array.isArray(feature.geometry.coordinates)).toBe(true); + }); + + test('should handle vessel tracks with large time gaps', () => { + const repData = `220118 120000.000 VESSEL @ 50 0 0.000 N 0 0 0.000 E 0 0 0 +220119 120000.000 VESSEL @ 51 0 0.000 N 1 0 0.000 E 0 0 0`; + + const result = window.ToolVault.tools.importREP(repData); + + expect(result.features).toHaveLength(1); // Same vessel + expect(result.features[0].geometry.coordinates).toHaveLength(2); + + const timestamps = result.features[0].properties.timestamps; + expect(timestamps[1] - timestamps[0]).toBe(86400000); // 24 hours in milliseconds + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/processing/smooth-polyline.test.js b/examples/javascript-bundle/tests/processing/smooth-polyline.test.js new file mode 100644 index 0000000..951b5c4 --- /dev/null +++ b/examples/javascript-bundle/tests/processing/smooth-polyline.test.js @@ -0,0 +1,229 @@ +// Load tool +require('../../tools/processing/smooth-polyline.js'); +const { sampleLineString } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Smooth Polyline', () => { + test('should smooth LineString geometry with Gaussian algorithm', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + const result = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'gaussian', + window_size: 3 + }); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.type).toBe('LineString'); + expect(result.geometry.coordinates).toHaveLength(input.geometry.coordinates.length); + + // Smoothed coordinates should be different from original + const originalCoords = input.geometry.coordinates; + const smoothedCoords = result.geometry.coordinates; + expect(smoothedCoords[1]).not.toEqual(originalCoords[1]); + }); + + test('should smooth LineString geometry with moving average algorithm', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + const result = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'moving_average', + window_size: 3 + }); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.type).toBe('LineString'); + expect(result.geometry.coordinates).toHaveLength(input.geometry.coordinates.length); + }); + + test('should handle FeatureCollection input', () => { + const input = { + type: 'FeatureCollection', + features: [ + JSON.parse(JSON.stringify(sampleLineString)) + ] + }; + const result = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'gaussian', + window_size: 3 + }); + + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(1); + expect(result.features[0].geometry.type).toBe('LineString'); + }); + + test('should handle direct LineString geometry input', () => { + const input = { + type: 'LineString', + coordinates: [ + [0, 0], + [1, 1], + [2, 0], + [3, 1], + [4, 0] + ] + }; + const result = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'gaussian', + window_size: 3 + }); + + expect(result.type).toBe('LineString'); + expect(result.coordinates).toHaveLength(5); + }); + + test('should use default parameters when none provided', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + const result = window.ToolVault.tools.smoothPolyline(input); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.type).toBe('LineString'); + // Should default to gaussian with window_size 3 + }); + + test('should handle different window sizes', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + + const small = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'gaussian', + window_size: 1 + }); + const large = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'gaussian', + window_size: 5 + }); + + expect(small.geometry.coordinates).toHaveLength(input.geometry.coordinates.length); + expect(large.geometry.coordinates).toHaveLength(input.geometry.coordinates.length); + }); + + test('should handle LineString with insufficient points for smoothing', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [1, 1] + ] + } + }; + const result = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'gaussian', + window_size: 5 + }); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.coordinates).toHaveLength(2); + // Should return original coordinates when insufficient points + }); + + test('should handle empty LineString', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [] + } + }; + const result = window.ToolVault.tools.smoothPolyline(input); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.coordinates).toHaveLength(0); + }); + + test('should handle single point LineString', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [[0, 0]] + } + }; + const result = window.ToolVault.tools.smoothPolyline(input); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.coordinates).toHaveLength(1); + expect(result.geometry.coordinates[0]).toEqual([0, 0]); + }); + + test('should preserve properties when smoothing Feature', () => { + const input = { + type: 'Feature', + properties: { + name: 'Test Track', + color: 'red' + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [1, 1], + [2, 0], + [3, 1] + ] + } + }; + const result = window.ToolVault.tools.smoothPolyline(input); + + expect(result.properties).toEqual(input.properties); + }); + + test('should handle invalid algorithm gracefully', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + const result = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'invalid_algorithm', + window_size: 3 + }); + + expect(result).toBeValidGeoJSON(); + // Should default to gaussian when invalid algorithm provided + expect(result.geometry.coordinates).toHaveLength(input.geometry.coordinates.length); + }); + + test('should handle non-LineString geometries by returning them unchanged', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'Point', + coordinates: [0, 0] + } + }; + const result = window.ToolVault.tools.smoothPolyline(input); + + expect(result).toEqual(input); + }); + + test('should produce different results for different algorithms', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [1, 2], + [2, 0], + [3, 2], + [4, 0] + ] + } + }; + + const gaussianResult = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'gaussian', + window_size: 3 + }); + const movingAvgResult = window.ToolVault.tools.smoothPolyline(input, { + algorithm: 'moving_average', + window_size: 3 + }); + + // The middle coordinates should be different between algorithms + expect(gaussianResult.geometry.coordinates[2]).not.toEqual(movingAvgResult.geometry.coordinates[2]); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/setup.js b/examples/javascript-bundle/tests/setup.js new file mode 100644 index 0000000..cfffbfe --- /dev/null +++ b/examples/javascript-bundle/tests/setup.js @@ -0,0 +1,31 @@ +// Jest setup file for ToolVault JavaScript Bundle +// Sets up the global ToolVault namespace and common test utilities + +// Initialize global ToolVault namespace +global.window = global.window || {}; +global.window.ToolVault = { + tools: {}, + metadata: {} +}; + +// Global test helpers +global.expect.extend({ + toBeValidGeoJSON(received) { + if (!received || typeof received !== 'object') { + return { + pass: false, + message: () => 'Expected valid GeoJSON object' + }; + } + + const validTypes = ['Feature', 'FeatureCollection', 'Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon']; + const hasValidType = validTypes.includes(received.type); + + return { + pass: hasValidType, + message: () => hasValidType ? + 'Expected invalid GeoJSON but received valid GeoJSON' : + `Expected valid GeoJSON type, received: ${received.type}` + }; + } +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/statistics/average-speed.test.js b/examples/javascript-bundle/tests/statistics/average-speed.test.js new file mode 100644 index 0000000..22f20e5 --- /dev/null +++ b/examples/javascript-bundle/tests/statistics/average-speed.test.js @@ -0,0 +1,163 @@ +// Load tool +require('../../tools/statistics/average-speed.js'); +const { sampleTrackWithTimestamps } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Calculate Average Speed', () => { + test('should calculate average speed from GPS track', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.calculateAverageSpeed(input, { time_unit: 'seconds' }); + + expect(typeof result).toBe('number'); + expect(result).toBeGreaterThan(0); + }); + + test('should handle different time units', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + + const secondsResult = window.ToolVault.tools.calculateAverageSpeed(input, { time_unit: 'seconds' }); + const minutesResult = window.ToolVault.tools.calculateAverageSpeed(input, { time_unit: 'minutes' }); + const hoursResult = window.ToolVault.tools.calculateAverageSpeed(input, { time_unit: 'hours' }); + + expect(secondsResult).toBeGreaterThan(minutesResult); + expect(minutesResult).toBeGreaterThan(hoursResult); + }); + + test('should handle direct LineString geometry', () => { + const input = { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ], + properties: { + timestamps: [1642505200000, 1642505260000] // 60 second interval + } + }; + const result = window.ToolVault.tools.calculateAverageSpeed(input); + + expect(typeof result).toBe('number'); + expect(result).toBeGreaterThan(0); + }); + + test('should use default parameters when none provided', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.calculateAverageSpeed(input); + + expect(typeof result).toBe('number'); + expect(result).toBeGreaterThan(0); + }); + + test('should handle track with timestamps in properties', () => { + const input = { + type: 'Feature', + properties: { + name: 'Test Track', + timestamps: [ + 1642505200000, + 1642505260000, + 1642505320000 + ] + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076], + [-0.1280, 51.5078] + ] + } + }; + const result = window.ToolVault.tools.calculateAverageSpeed(input); + + expect(typeof result).toBe('number'); + expect(result).toBeGreaterThan(0); + }); + + test('should handle track with insufficient points', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000] + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074] + ] + } + }; + const result = window.ToolVault.tools.calculateAverageSpeed(input); + + expect(result).toBe(0); + }); + + test('should handle track with no timestamps', () => { + const input = { + type: 'Feature', + properties: {}, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ] + } + }; + const result = window.ToolVault.tools.calculateAverageSpeed(input); + + expect(result).toBe(0); + }); + + test('should handle invalid time_unit', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.calculateAverageSpeed(input, { time_unit: 'invalid' }); + + expect(typeof result).toBe('number'); + // Should default to seconds + expect(result).toBeGreaterThan(0); + }); + + test('should handle zero time intervals', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000, 1642505200000] // Same timestamp + }, + geometry: { + type: 'LineString', + coordinates: [ + [-0.1276, 51.5074], + [-0.1278, 51.5076] + ] + } + }; + const result = window.ToolVault.tools.calculateAverageSpeed(input); + + expect(result).toBe(0); + }); + + test('should calculate realistic speeds', () => { + // Create a track with known distance and time + const input = { + type: 'Feature', + properties: { + timestamps: [0, 1000] // 1 second apart + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [0, 0.009] // Approximately 1km north + ] + } + }; + const result = window.ToolVault.tools.calculateAverageSpeed(input, { time_unit: 'seconds' }); + + // Should be roughly 1000 m/s (very fast, but mathematically correct) + expect(result).toBeGreaterThan(900); + expect(result).toBeLessThan(1100); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/statistics/speed-histogram.test.js b/examples/javascript-bundle/tests/statistics/speed-histogram.test.js new file mode 100644 index 0000000..41b3493 --- /dev/null +++ b/examples/javascript-bundle/tests/statistics/speed-histogram.test.js @@ -0,0 +1,148 @@ +// Load tools - histogram depends on speed-series +require('../../tools/analysis/speed-series.js'); +require('../../tools/statistics/speed-histogram.js'); +const { sampleTrackWithTimestamps } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Create Speed Histogram', () => { + test('should create speed histogram from GPS track', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.createSpeedHistogram(input, { bins: 5 }); + + expect(typeof result).toBe('object'); + expect(result).toHaveProperty('bins'); + expect(result).toHaveProperty('counts'); + expect(result).toHaveProperty('min'); + expect(result).toHaveProperty('max'); + expect(result).toHaveProperty('total'); + + expect(Array.isArray(result.bins)).toBe(true); + expect(Array.isArray(result.counts)).toBe(true); + expect(result.bins.length).toBe(5); + expect(result.counts.length).toBe(5); + }); + + test('should handle different bin counts', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + + const result5 = window.ToolVault.tools.createSpeedHistogram(input, { bins: 5 }); + const result10 = window.ToolVault.tools.createSpeedHistogram(input, { bins: 10 }); + + expect(result5.bins.length).toBe(5); + expect(result10.bins.length).toBe(10); + }); + + test('should use default parameters when none provided', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.createSpeedHistogram(input); + + expect(result.bins.length).toBe(10); // Default bins + expect(result.counts.length).toBe(10); + }); + + test('should handle different time units', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + + const secondsResult = window.ToolVault.tools.createSpeedHistogram(input, { + bins: 5, + time_unit: 'seconds' + }); + const minutesResult = window.ToolVault.tools.createSpeedHistogram(input, { + bins: 5, + time_unit: 'minutes' + }); + + // Minutes should have smaller speeds than seconds + expect(minutesResult.max).toBeLessThan(secondsResult.max); + }); + + test('should handle track with no movement', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [1642505200000, 1642505260000] + }, + geometry: { + type: 'LineString', + coordinates: [ + [0, 0], + [0, 0] // Same coordinates + ] + } + }; + const result = window.ToolVault.tools.createSpeedHistogram(input); + + expect(result.min).toBe(0); + expect(result.max).toBe(0); + expect(result.total).toBe(1); + }); + + test('should handle empty track', () => { + const input = { + type: 'Feature', + properties: { + timestamps: [] + }, + geometry: { + type: 'LineString', + coordinates: [] + } + }; + const result = window.ToolVault.tools.createSpeedHistogram(input); + + expect(result.bins.length).toBe(0); + expect(result.counts.length).toBe(0); + expect(result.total).toBe(0); + }); + + test('should throw error when speed-series tool not available', () => { + // Temporarily remove the speed series tool + const originalTool = window.ToolVault.tools.calculateSpeedSeries; + delete window.ToolVault.tools.calculateSpeedSeries; + + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + + expect(() => { + window.ToolVault.tools.createSpeedHistogram(input); + }).toThrow('Speed series tool not available'); + + // Restore the tool + window.ToolVault.tools.calculateSpeedSeries = originalTool; + }); + + test('should have correct bin structure', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.createSpeedHistogram(input, { bins: 3 }); + + result.bins.forEach(bin => { + expect(bin).toHaveProperty('min'); + expect(bin).toHaveProperty('max'); + expect(bin).toHaveProperty('center'); + expect(typeof bin.min).toBe('number'); + expect(typeof bin.max).toBe('number'); + expect(typeof bin.center).toBe('number'); + expect(bin.max).toBeGreaterThanOrEqual(bin.min); + expect(bin.center).toBeGreaterThanOrEqual(bin.min); + expect(bin.center).toBeLessThanOrEqual(bin.max); + }); + }); + + test('should have counts that sum to total', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.createSpeedHistogram(input, { bins: 5 }); + + const countsSum = result.counts.reduce((sum, count) => sum + count, 0); + expect(countsSum).toBe(result.total); + }); + + test('should handle single bin', () => { + const input = JSON.parse(JSON.stringify(sampleTrackWithTimestamps)); + const result = window.ToolVault.tools.createSpeedHistogram(input, { bins: 1 }); + + expect(result.bins.length).toBe(1); + expect(result.counts.length).toBe(1); + expect(result.counts[0]).toBe(result.total); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/transform/flip.test.js b/examples/javascript-bundle/tests/transform/flip.test.js new file mode 100644 index 0000000..692b187 --- /dev/null +++ b/examples/javascript-bundle/tests/transform/flip.test.js @@ -0,0 +1,114 @@ +// Load tools +require('../../tools/transform/flip-horizontal.js'); +require('../../tools/transform/flip-vertical.js'); +const { samplePoint, sampleFeatureCollection, sampleLineString } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Flip Features', () => { + describe('Flip Horizontal', () => { + test('should flip Point feature horizontally around longitude center', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const originalLon = input.geometry.coordinates[0]; + const result = window.ToolVault.tools.flipHorizontal(input, { axis: 'longitude' }); + + expect(result).toBeValidGeoJSON(); + expect(result.type).toBe('Feature'); + expect(result.geometry.type).toBe('Point'); + + // Longitude should change, latitude should remain same + expect(result.geometry.coordinates[0]).not.toBe(originalLon); + expect(result.geometry.coordinates[1]).toBe(input.geometry.coordinates[1]); + }); + + test('should flip FeatureCollection horizontally', () => { + const input = JSON.parse(JSON.stringify(sampleFeatureCollection)); + const result = window.ToolVault.tools.flipHorizontal(input, { axis: 'longitude' }); + + expect(result).toBeValidGeoJSON(); + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(2); + + // Should maintain same structure + expect(result.features[0].geometry.type).toBe('Point'); + expect(result.features[1].geometry.type).toBe('LineString'); + }); + + test('should handle latitude axis flipping', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const originalLat = input.geometry.coordinates[1]; + const result = window.ToolVault.tools.flipHorizontal(input, { axis: 'latitude' }); + + expect(result).toBeValidGeoJSON(); + // Latitude should change, longitude should remain same + expect(result.geometry.coordinates[1]).not.toBe(originalLat); + expect(result.geometry.coordinates[0]).toBe(input.geometry.coordinates[0]); + }); + + test('should handle LineString geometry', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + const result = window.ToolVault.tools.flipHorizontal(input, { axis: 'longitude' }); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.type).toBe('LineString'); + expect(result.geometry.coordinates).toHaveLength(3); + + // Coordinates should be flipped (though some may remain the same if they were at center) + const originalLongitudes = input.geometry.coordinates.map(c => c[0]); + const flippedLongitudes = result.geometry.coordinates.map(c => c[0]); + + // At least one coordinate should be different (unless all were at exact center) + const hasDifferentCoord = flippedLongitudes.some((lon, index) => lon !== originalLongitudes[index]); + expect(hasDifferentCoord || originalLongitudes.every(lon => lon === originalLongitudes[0])).toBe(true); + + // Latitudes should remain unchanged + result.geometry.coordinates.forEach((coord, index) => { + expect(coord[1]).toBe(input.geometry.coordinates[index][1]); + }); + }); + + test('should use default axis when none provided', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const result = window.ToolVault.tools.flipHorizontal(input); + + expect(result).toBeValidGeoJSON(); + // Should default to longitude axis + expect(result.geometry.coordinates[0]).not.toBe(input.geometry.coordinates[0]); + }); + + test('should handle direct geometry input', () => { + const input = { + type: 'Point', + coordinates: [1, 2] + }; + const result = window.ToolVault.tools.flipHorizontal(input); + + expect(result.type).toBe('Point'); + expect(result.coordinates[0]).toBe(-1); // Flipped around center (0) + expect(result.coordinates[1]).toBe(2); + }); + }); + + describe('Flip Vertical', () => { + test('should flip Point feature vertically', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const originalLat = input.geometry.coordinates[1]; + const result = window.ToolVault.tools.flipVertical(input); + + expect(result).toBeValidGeoJSON(); + // Should flip latitude, preserve longitude + expect(result.geometry.coordinates[1]).not.toBe(originalLat); + expect(result.geometry.coordinates[0]).toBe(input.geometry.coordinates[0]); + }); + + test('should handle FeatureCollection', () => { + const input = JSON.parse(JSON.stringify(sampleFeatureCollection)); + const result = window.ToolVault.tools.flipVertical(input); + + expect(result).toBeValidGeoJSON(); + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(2); + }); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tests/transform/translate.test.js b/examples/javascript-bundle/tests/transform/translate.test.js new file mode 100644 index 0000000..fa77db2 --- /dev/null +++ b/examples/javascript-bundle/tests/transform/translate.test.js @@ -0,0 +1,91 @@ +// Load tool +require('../../tools/transform/translate.js'); +const { samplePoint, sampleFeatureCollection, sampleLineString } = require('../helpers'); + +// Initialize window object +global.window = global.window || {}; + +describe('Translate Features', () => { + test('should translate Point feature by specified distance and direction', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const params = { distance: 1000, direction: 90, units: 'meters' }; // 1km east + const result = window.ToolVault.tools.translate(input, params); + + expect(result).toBeValidGeoJSON(); + expect(result.type).toBe('Feature'); + expect(result.geometry.type).toBe('Point'); + + // Should move east (increase longitude) + expect(result.geometry.coordinates[0]).toBeGreaterThan(input.geometry.coordinates[0]); + expect(result.geometry.coordinates[1]).toBeCloseTo(input.geometry.coordinates[1], 5); + }); + + test('should translate FeatureCollection', () => { + const input = JSON.parse(JSON.stringify(sampleFeatureCollection)); + const params = { distance: 500, direction: 180, units: 'meters' }; // 500m south + const result = window.ToolVault.tools.translate(input, params); + + expect(result).toBeValidGeoJSON(); + expect(result.type).toBe('FeatureCollection'); + expect(result.features).toHaveLength(2); + + // All features should move south (decrease latitude) + result.features.forEach((feature, index) => { + const originalFeature = input.features[index]; + if (feature.geometry.type === 'Point') { + expect(feature.geometry.coordinates[1]).toBeLessThan(originalFeature.geometry.coordinates[1]); + } else if (feature.geometry.type === 'LineString') { + feature.geometry.coordinates.forEach((coord, coordIndex) => { + expect(coord[1]).toBeLessThan(originalFeature.geometry.coordinates[coordIndex][1]); + }); + } + }); + }); + + test('should handle kilometers unit conversion', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const params = { distance: 1, direction: 0, units: 'kilometers' }; // 1km north + const result = window.ToolVault.tools.translate(input, params); + + expect(result).toBeValidGeoJSON(); + // Should move north (increase latitude) + expect(result.geometry.coordinates[1]).toBeGreaterThan(input.geometry.coordinates[1]); + expect(result.geometry.coordinates[0]).toBeCloseTo(input.geometry.coordinates[0], 5); + }); + + test('should handle direct geometry input', () => { + const input = { + type: 'Point', + coordinates: [0, 0] + }; + const params = { distance: 1000, direction: 45, units: 'meters' }; // NE + const result = window.ToolVault.tools.translate(input, params); + + expect(result.type).toBe('Point'); + expect(result.coordinates[0]).toBeGreaterThan(0); // East + expect(result.coordinates[1]).toBeGreaterThan(0); // North + }); + + test('should use default parameters when none provided', () => { + const input = JSON.parse(JSON.stringify(samplePoint)); + const result = window.ToolVault.tools.translate(input); + + // With distance=0, coordinates should remain the same + expect(result.geometry.coordinates).toEqual(input.geometry.coordinates); + }); + + test('should handle LineString geometry', () => { + const input = JSON.parse(JSON.stringify(sampleLineString)); + const params = { distance: 100, direction: 270, units: 'meters' }; // 100m west + const result = window.ToolVault.tools.translate(input, params); + + expect(result).toBeValidGeoJSON(); + expect(result.geometry.type).toBe('LineString'); + + // All coordinates should move west (decrease longitude) + result.geometry.coordinates.forEach((coord, index) => { + expect(coord[0]).toBeLessThan(input.geometry.coordinates[index][0]); + expect(coord[1]).toBeCloseTo(input.geometry.coordinates[index][1], 5); + }); + }); +}); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/analysis/direction-series.js b/examples/javascript-bundle/tools/analysis/direction-series.js new file mode 100644 index 0000000..5b2ee72 --- /dev/null +++ b/examples/javascript-bundle/tools/analysis/direction-series.js @@ -0,0 +1,77 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.calculateDirectionSeries = function(input, params) { + const { smoothing = false, window_size = 3 } = params || {}; + + // Calculate bearing between two points + function calculateBearing(lat1, lon1, lat2, lon2) { + const φ1 = lat1 * Math.PI / 180; + const φ2 = lat2 * Math.PI / 180; + const Δλ = (lon2 - lon1) * Math.PI / 180; + + const y = Math.sin(Δλ) * Math.cos(φ2); + const x = Math.cos(φ1) * Math.sin(φ2) - + Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ); + + const θ = Math.atan2(y, x); + + return ((θ * 180 / Math.PI) + 360) % 360; // Convert to degrees and normalize to 0-360 + } + + // Extract coordinates and timestamps + let coordinates = []; + let timestamps = []; + + if (input.type === 'Feature' && input.geometry.type === 'LineString') { + coordinates = input.geometry.coordinates; + timestamps = input.properties?.timestamps || []; + } else if (input.type === 'LineString') { + coordinates = input.coordinates; + timestamps = input.properties?.timestamps || []; + } + + // Calculate directions + const rawSeries = []; + + for (let i = 1; i < coordinates.length && i < timestamps.length; i++) { + const coord1 = coordinates[i - 1]; + const coord2 = coordinates[i]; + + const bearing = calculateBearing(coord1[1], coord1[0], coord2[1], coord2[0]); + + rawSeries.push({ + time: timestamps[i], + direction: bearing + }); + } + + // Apply smoothing if requested + if (smoothing && window_size > 1) { + const smoothedSeries = []; + const halfWindow = Math.floor(window_size / 2); + + for (let i = 0; i < rawSeries.length; i++) { + let sum = 0; + let count = 0; + + for (let j = Math.max(0, i - halfWindow); + j <= Math.min(rawSeries.length - 1, i + halfWindow); + j++) { + sum += rawSeries[j].direction; + count++; + } + + smoothedSeries.push({ + time: rawSeries[i].time, + direction: sum / count + }); + } + + return smoothedSeries; + } + + return rawSeries; + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/analysis/speed-series.js b/examples/javascript-bundle/tools/analysis/speed-series.js new file mode 100644 index 0000000..3c9337e --- /dev/null +++ b/examples/javascript-bundle/tools/analysis/speed-series.js @@ -0,0 +1,70 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.calculateSpeedSeries = function(input, params) { + const { time_unit = 'seconds' } = params || {}; + + // Haversine formula to calculate distance between two points + function haversineDistance(lat1, lon1, lat2, lon2) { + const R = 6371000; // Earth radius in meters + const φ1 = lat1 * Math.PI / 180; + const φ2 = lat2 * Math.PI / 180; + const Δφ = (lat2 - lat1) * Math.PI / 180; + const Δλ = (lon2 - lon1) * Math.PI / 180; + + const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + + Math.cos(φ1) * Math.cos(φ2) * + Math.sin(Δλ/2) * Math.sin(Δλ/2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + + return R * c; // Distance in meters + } + + // Extract coordinates and timestamps + let coordinates = []; + let timestamps = []; + + if (input.type === 'Feature' && input.geometry.type === 'LineString') { + coordinates = input.geometry.coordinates; + timestamps = input.properties?.timestamps || []; + } else if (input.type === 'LineString') { + coordinates = input.coordinates; + timestamps = input.properties?.timestamps || []; + } + + // Calculate speeds + const series = []; + + for (let i = 1; i < coordinates.length && i < timestamps.length; i++) { + const coord1 = coordinates[i - 1]; + const coord2 = coordinates[i]; + const time1 = new Date(timestamps[i - 1]); + const time2 = new Date(timestamps[i]); + + // Calculate distance in meters + const distance = haversineDistance(coord1[1], coord1[0], coord2[1], coord2[0]); + + // Calculate time difference in seconds + const timeDiff = (time2 - time1) / 1000; + + if (timeDiff > 0) { + // Calculate speed based on requested unit + let speed = distance / timeDiff; // m/s + + if (time_unit === 'minutes') { + speed = speed / 60; // m/min + } else if (time_unit === 'hours') { + speed = speed / 3600; // m/h + } + + series.push({ + time: timestamps[i], + speed: speed + }); + } + } + + return series; + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/io/export-csv.js b/examples/javascript-bundle/tools/io/export-csv.js new file mode 100644 index 0000000..cc20572 --- /dev/null +++ b/examples/javascript-bundle/tools/io/export-csv.js @@ -0,0 +1,165 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.exportCSV = function(input, params) { + const { include_properties = false, coordinate_format = 'separate', separator = ',' } = params || {}; + + const rows = []; + const headers = []; + + // Helper function to escape CSV values + function escapeCSV(value) { + if (value === null || value === undefined) { + return ''; + } + value = String(value); + if (value.includes(separator) || value.includes('"') || value.includes('\n')) { + return '"' + value.replace(/"/g, '""') + '"'; + } + return value; + } + + // Helper function to convert geometry to WKT + function geometryToWKT(geometry) { + if (geometry.type === 'Point') { + return `POINT(${geometry.coordinates[0]} ${geometry.coordinates[1]})`; + } else if (geometry.type === 'LineString') { + const coords = geometry.coordinates + .map(c => `${c[0]} ${c[1]}`) + .join(','); + return `LINESTRING(${coords})`; + } else if (geometry.type === 'Polygon') { + const rings = geometry.coordinates + .map(ring => { + const coords = ring.map(c => `${c[0]} ${c[1]}`).join(','); + return `(${coords})`; + }) + .join(','); + return `POLYGON${rings}`; + } else if (geometry.type === 'MultiPoint') { + const points = geometry.coordinates + .map(c => `${c[0]} ${c[1]}`) + .join(','); + return `MULTIPOINT(${points})`; + } + return ''; + } + + // Helper function to flatten geometry coordinates + function flattenCoordinates(geometry) { + const points = []; + + if (geometry.type === 'Point') { + points.push(geometry.coordinates); + } else if (geometry.type === 'LineString') { + points.push(...geometry.coordinates); + } else if (geometry.type === 'Polygon') { + points.push(...geometry.coordinates[0]); // Only outer ring + } else if (geometry.type === 'MultiPoint') { + points.push(...geometry.coordinates); + } + + return points; + } + + // Process features + let features = []; + + if (input.type === 'FeatureCollection') { + features = input.features; + } else if (input.type === 'Feature') { + features = [input]; + } else if (input.type && input.coordinates) { + // Direct geometry + features = [{ + type: 'Feature', + properties: {}, + geometry: input + }]; + } + + // Check for timestamps - include when present + const hasTimestamps = features.some(f => f.properties?.timestamps && Array.isArray(f.properties.timestamps)); + const shouldIncludeTimestamps = hasTimestamps; + + // Collect all property keys for headers (excluding timestamps which get special treatment) + const propertyKeys = new Set(); + + if (include_properties) { + features.forEach(feature => { + if (feature.properties) { + Object.keys(feature.properties).forEach(key => { + if (key !== 'timestamps') { // Handle timestamps separately + propertyKeys.add(key); + } + }); + } + }); + } + + // Build headers + if (coordinate_format === 'wkt') { + headers.push('geometry'); + } else { + headers.push('longitude', 'latitude'); + } + + // Add timestamp header if timestamps should be included + if (shouldIncludeTimestamps) { + headers.push('timestamp'); + } + + if (include_properties) { + headers.push(...Array.from(propertyKeys)); + } + + rows.push(headers.map(escapeCSV).join(separator)); + + // Process each feature + features.forEach(feature => { + if (!feature.geometry) { + return; + } + + if (coordinate_format === 'wkt') { + // Single row per feature with WKT geometry + const row = []; + row.push(escapeCSV(geometryToWKT(feature.geometry))); + + if (include_properties && feature.properties) { + propertyKeys.forEach(key => { + row.push(escapeCSV(feature.properties[key])); + }); + } + + rows.push(row.join(separator)); + } else { + // One row per coordinate pair + const coordinates = flattenCoordinates(feature.geometry); + const timestamps = feature.properties?.timestamps || []; + + coordinates.forEach((coord, index) => { + const row = []; + row.push(escapeCSV(coord[0])); // longitude + row.push(escapeCSV(coord[1])); // latitude + + // Add timestamp if available for this coordinate + if (shouldIncludeTimestamps) { + row.push(escapeCSV(timestamps[index] || '')); + } + + if (include_properties && feature.properties) { + propertyKeys.forEach(key => { + row.push(escapeCSV(feature.properties[key])); + }); + } + + rows.push(row.join(separator)); + }); + } + }); + + return rows.join('\n') + (rows.length === 1 ? '\n' : ''); + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/io/export-rep.js b/examples/javascript-bundle/tools/io/export-rep.js new file mode 100644 index 0000000..607d88e --- /dev/null +++ b/examples/javascript-bundle/tools/io/export-rep.js @@ -0,0 +1,172 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.exportREP = function(input, params) { + const { precision = 6 } = params || {}; + + // REP format output - actual Debrief REP format + let output = []; + + // Helper function to convert decimal degrees to degrees, minutes, seconds + function convertToDMS(decimal) { + const abs = Math.abs(decimal); + const degrees = Math.floor(abs); + const minutesFloat = (abs - degrees) * 60; + const minutes = Math.floor(minutesFloat); + const seconds = (minutesFloat - minutes) * 60; + return { + degrees: degrees, + minutes: minutes, + seconds: parseFloat(seconds.toFixed(precision)) + }; + } + + // Helper function to format timestamp for REP format + function formatREPTimestamp(timestamp) { + const date = new Date(timestamp); + const year = date.getUTCFullYear().toString().slice(2); // YY format + const month = String(date.getUTCMonth() + 1).padStart(2, '0'); + const day = String(date.getUTCDate()).padStart(2, '0'); + const hours = String(date.getUTCHours()).padStart(2, '0'); + const minutes = String(date.getUTCMinutes()).padStart(2, '0'); + const seconds = String(date.getUTCSeconds()).padStart(2, '0'); + const ms = String(date.getUTCMilliseconds()).padStart(3, '0'); + + return { + date: `${year}${month}${day}`, + time: `${hours}${minutes}${seconds}.${ms}` + }; + } + + // Helper function to create REP track point + function createREPPoint(lon, lat, timestamp, vesselName = 'UNKNOWN', heading = 0, speed = 0, depth = 0) { + const latDMS = convertToDMS(lat); + const lonDMS = convertToDMS(lon); + const latHem = lat >= 0 ? 'N' : 'S'; + const lonHem = lon >= 0 ? 'E' : 'W'; + + let dateStr, timeStr; + if (timestamp) { + const repTime = formatREPTimestamp(timestamp); + dateStr = repTime.date; + timeStr = repTime.time; + } else { + // Default to epoch start if no timestamp + dateStr = '700101'; + timeStr = '000000.000'; + } + + // REP format: YYMMDD HHMMSS.SSS vessel_name symbol lat_deg lat_min lat_sec lat_hem lon_deg lon_min lon_sec lon_hem heading speed depth + return `${dateStr} ${timeStr} ${vesselName} @ ${latDMS.degrees} ${latDMS.minutes} ${latDMS.seconds} ${latHem} ${lonDMS.degrees} ${lonDMS.minutes} ${lonDMS.seconds} ${lonHem} ${heading} ${speed} ${depth}`; + } + + // Process input based on type + if (input.type === 'FeatureCollection') { + input.features.forEach((feature) => { + if (feature.geometry) { + const vesselName = feature.properties?.vessel_name || feature.properties?.name || 'UNKNOWN'; + const timestamps = feature.properties?.timestamps || []; + const headings = feature.properties?.heading || feature.properties?.headings || []; + const speeds = feature.properties?.speed || feature.properties?.speeds || []; + const depths = feature.properties?.depth || feature.properties?.depths || []; + + if (feature.geometry.type === 'Point') { + const [lon, lat] = feature.geometry.coordinates; + const timestamp = timestamps[0]; + const heading = headings[0] || 0; + const speed = speeds[0] || 0; + const depth = depths[0] || 0; + if (timestamp !== undefined) { + output.push(createREPPoint(lon, lat, timestamp, vesselName, heading, speed, depth)); + } + } else if (feature.geometry.type === 'LineString') { + feature.geometry.coordinates.forEach((coord, index) => { + const [lon, lat] = coord; + const timestamp = timestamps[index]; + const heading = headings[index] || 0; + const speed = speeds[index] || 0; + const depth = depths[index] || 0; + if (timestamp !== undefined) { + output.push(createREPPoint(lon, lat, timestamp, vesselName, heading, speed, depth)); + } + }); + } else if (feature.geometry.type === 'Polygon') { + // Export outer ring only + feature.geometry.coordinates[0].forEach((coord, index) => { + const [lon, lat] = coord; + const timestamp = timestamps[index]; + const heading = headings[index] || 0; + const speed = speeds[index] || 0; + const depth = depths[index] || 0; + if (timestamp !== undefined) { + output.push(createREPPoint(lon, lat, timestamp, vesselName, heading, speed, depth)); + } + }); + } + } + }); + } else if (input.type === 'Feature') { + const vesselName = input.properties?.vessel_name || input.properties?.name || 'UNKNOWN'; + const timestamps = input.properties?.timestamps || []; + const headings = input.properties?.heading || input.properties?.headings || []; + const speeds = input.properties?.speed || input.properties?.speeds || []; + const depths = input.properties?.depth || input.properties?.depths || []; + + if (input.geometry.type === 'Point') { + const [lon, lat] = input.geometry.coordinates; + const timestamp = timestamps[0]; + const heading = headings[0] || 0; + const speed = speeds[0] || 0; + const depth = depths[0] || 0; + if (timestamp !== undefined) { + output.push(createREPPoint(lon, lat, timestamp, vesselName, heading, speed, depth)); + } + } else if (input.geometry.type === 'LineString') { + input.geometry.coordinates.forEach((coord, index) => { + const [lon, lat] = coord; + const timestamp = timestamps[index]; + const heading = headings[index] || 0; + const speed = speeds[index] || 0; + const depth = depths[index] || 0; + if (timestamp !== undefined) { + output.push(createREPPoint(lon, lat, timestamp, vesselName, heading, speed, depth)); + } + }); + } else if (input.geometry.type === 'Polygon') { + // Export outer ring only + input.geometry.coordinates[0].forEach((coord, index) => { + const [lon, lat] = coord; + const timestamp = timestamps[index]; + const heading = headings[index] || 0; + const speed = speeds[index] || 0; + const depth = depths[index] || 0; + if (timestamp !== undefined) { + output.push(createREPPoint(lon, lat, timestamp, vesselName, heading, speed, depth)); + } + }); + } + } else if (input.type && input.coordinates) { + // Direct geometry object + const vesselName = 'UNKNOWN'; + + if (input.type === 'Point') { + const [lon, lat] = input.coordinates; + output.push(createREPPoint(lon, lat, null, vesselName)); + } else if (input.type === 'LineString') { + input.coordinates.forEach((coord) => { + const [lon, lat] = coord; + output.push(createREPPoint(lon, lat, null, vesselName)); + }); + } else if (input.type === 'Polygon') { + // Export outer ring only + input.coordinates[0].forEach((coord) => { + const [lon, lat] = coord; + output.push(createREPPoint(lon, lat, null, vesselName)); + }); + } + } + + return output.join('\n'); + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/io/import-rep.js b/examples/javascript-bundle/tools/io/import-rep.js new file mode 100644 index 0000000..1f434e0 --- /dev/null +++ b/examples/javascript-bundle/tools/io/import-rep.js @@ -0,0 +1,176 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.importREP = function(input, params) { + const { encoding = 'utf-8' } = params || {}; + + // Debrief REP format parser + // Format: YYMMDD HHMMSS.SSS vessel_name symbol lat_deg lat_min lat_sec lat_hem lon_deg lon_min lon_sec lon_hem heading speed depth [label] + // Lines starting with ; are comments (ignored) + // Multiple tracks distinguished by vessel name + + const lines = input.split('\n'); + const vesselTracks = new Map(); // vessel name -> array of points + + // Helper function to convert DMS to decimal degrees + function convertFromDMS(degrees, minutes, seconds, hemisphere) { + let decimal = degrees + (minutes / 60) + (seconds / 3600); + if (hemisphere === 'S' || hemisphere === 'W') { + decimal = -decimal; + } + return decimal; + } + + // Helper function to parse REP timestamp + function parseREPTimestamp(dateStr, timeStr) { + // Handle both YY and YYYY formats + let year; + if (dateStr.length === 6) { + const yy = parseInt(dateStr.substring(0, 2)); + // Assume 70-99 = 1970-1999, 00-69 = 2000-2069 + year = yy >= 70 ? 1900 + yy : 2000 + yy; + } else { + year = parseInt(dateStr.substring(0, 4)); + } + + const month = parseInt(dateStr.substring(dateStr.length - 4, dateStr.length - 2)) - 1; // 0-indexed + const day = parseInt(dateStr.substring(dateStr.length - 2)); + + const hours = parseInt(timeStr.substring(0, 2)); + const minutes = parseInt(timeStr.substring(2, 4)); + const secondsFloat = parseFloat(timeStr.substring(4)); + const seconds = Math.floor(secondsFloat); + const milliseconds = Math.round((secondsFloat - seconds) * 1000); + + return Date.UTC(year, month, day, hours, minutes, seconds, milliseconds); + } + + for (let line of lines) { + line = line.trim(); + + // Skip empty lines and comments (starting with ;) + if (!line || line.startsWith(';')) { + continue; + } + + // Split by whitespace, handling quoted vessel names + const parts = []; + let current = ''; + let inQuotes = false; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + if (char === '"') { + inQuotes = !inQuotes; + } else if (char === ' ' && !inQuotes && current.length > 0) { + parts.push(current); + current = ''; + } else if (char !== ' ' || inQuotes) { + current += char; + } + } + if (current.length > 0) { + parts.push(current); + } + + // Minimum fields required for a valid track point + if (parts.length < 12) { + continue; + } + + try { + const dateStr = parts[0]; + const timeStr = parts[1]; + const vesselName = parts[2].replace(/"/g, ''); // Remove quotes + const symbol = parts[3]; + + const latDeg = parseInt(parts[4]); + const latMin = parseFloat(parts[5]); + const latSec = parseFloat(parts[6]); + const latHem = parts[7]; + + const lonDeg = parseInt(parts[8]); + const lonMin = parseFloat(parts[9]); + const lonSec = parseFloat(parts[10]); + const lonHem = parts[11]; + + const heading = parts.length > 12 ? parseFloat(parts[12]) || 0 : 0; + const speed = parts.length > 13 ? parseFloat(parts[13]) || 0 : 0; + const depth = parts.length > 14 ? parseFloat(parts[14]) || 0 : 0; + const label = parts.length > 15 ? parts.slice(15).join(' ') : ''; + + // Convert to decimal degrees + const lat = convertFromDMS(latDeg, latMin, latSec, latHem); + const lon = convertFromDMS(lonDeg, lonMin, lonSec, lonHem); + + // Parse timestamp + const timestamp = parseREPTimestamp(dateStr, timeStr); + + // Add to vessel track + if (!vesselTracks.has(vesselName)) { + vesselTracks.set(vesselName, []); + } + + vesselTracks.get(vesselName).push({ + coordinates: [lon, lat], + timestamp: timestamp, + heading: heading, + speed: speed, + depth: depth, + label: label + }); + + } catch (e) { + // Skip malformed lines + continue; + } + } + + // Create GeoJSON FeatureCollection + const features = []; + + for (const [vesselName, points] of vesselTracks.entries()) { + if (points.length === 0) continue; + + let geometry; + const timestamps = points.map(p => p.timestamp); + const properties = { + name: vesselName, + vessel_name: vesselName, + timestamps: timestamps + }; + + // Add additional data if available + if (points.some(p => p.heading !== 0)) { + properties.heading = points.map(p => p.heading); + } + if (points.some(p => p.speed !== 0)) { + properties.speed = points.map(p => p.speed); + } + if (points.some(p => p.depth !== 0)) { + properties.depth = points.map(p => p.depth); + } + if (points.some(p => p.label)) { + properties.labels = points.map(p => p.label); + } + + // Always create LineString for vessel tracks (even single points) + geometry = { + type: 'LineString', + coordinates: points.map(p => p.coordinates) + }; + + features.push({ + type: 'Feature', + properties: properties, + geometry: geometry + }); + } + + return { + type: 'FeatureCollection', + features: features + }; + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/processing/smooth-polyline.js b/examples/javascript-bundle/tools/processing/smooth-polyline.js new file mode 100644 index 0000000..d40d0f8 --- /dev/null +++ b/examples/javascript-bundle/tools/processing/smooth-polyline.js @@ -0,0 +1,108 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.smoothPolyline = function(input, params) { + const { algorithm = 'moving_average', window_size = 3, sigma = 1.0 } = params || {}; + + // Deep clone the input + const result = JSON.parse(JSON.stringify(input)); + + // Helper function to apply moving average smoothing + function movingAverageSmooth(coordinates, windowSize) { + if (coordinates.length <= windowSize) { + return coordinates; + } + + const smoothed = []; + const halfWindow = Math.floor(windowSize / 2); + + for (let i = 0; i < coordinates.length; i++) { + let sumLon = 0, sumLat = 0, count = 0; + + const start = Math.max(0, i - halfWindow); + const end = Math.min(coordinates.length - 1, i + halfWindow); + + for (let j = start; j <= end; j++) { + sumLon += coordinates[j][0]; + sumLat += coordinates[j][1]; + count++; + } + + smoothed.push([sumLon / count, sumLat / count]); + } + + return smoothed; + } + + // Helper function to apply Gaussian smoothing + function gaussianSmooth(coordinates, sigma) { + if (coordinates.length < 3) { + return coordinates; + } + + const smoothed = []; + const kernelSize = Math.ceil(3 * sigma) * 2 + 1; + const halfKernel = Math.floor(kernelSize / 2); + + // Generate Gaussian kernel + const kernel = []; + let kernelSum = 0; + for (let i = -halfKernel; i <= halfKernel; i++) { + const weight = Math.exp(-(i * i) / (2 * sigma * sigma)); + kernel.push(weight); + kernelSum += weight; + } + + // Normalize kernel + for (let i = 0; i < kernel.length; i++) { + kernel[i] /= kernelSum; + } + + // Apply convolution + for (let i = 0; i < coordinates.length; i++) { + let sumLon = 0, sumLat = 0; + + for (let j = 0; j < kernel.length; j++) { + const coordIndex = i - halfKernel + j; + + if (coordIndex >= 0 && coordIndex < coordinates.length) { + sumLon += coordinates[coordIndex][0] * kernel[j]; + sumLat += coordinates[coordIndex][1] * kernel[j]; + } + } + + smoothed.push([sumLon, sumLat]); + } + + return smoothed; + } + + // Apply smoothing to coordinates + function smoothCoordinates(coords) { + if (algorithm === 'moving_average') { + return movingAverageSmooth(coords, window_size); + } else if (algorithm === 'gaussian') { + return gaussianSmooth(coords, sigma); + } + return coords; + } + + // Process based on input type + if (result.type === 'FeatureCollection') { + result.features.forEach(feature => { + if (feature.geometry && feature.geometry.type === 'LineString') { + feature.geometry.coordinates = smoothCoordinates(feature.geometry.coordinates); + } + }); + } else if (result.type === 'Feature') { + if (result.geometry && result.geometry.type === 'LineString') { + result.geometry.coordinates = smoothCoordinates(result.geometry.coordinates); + } + } else if (result.type === 'LineString') { + result.coordinates = smoothCoordinates(result.coordinates); + } + + return result; + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/statistics/average-speed.js b/examples/javascript-bundle/tools/statistics/average-speed.js new file mode 100644 index 0000000..d143388 --- /dev/null +++ b/examples/javascript-bundle/tools/statistics/average-speed.js @@ -0,0 +1,68 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.calculateAverageSpeed = function(input, params) { + const { time_unit = 'seconds' } = params || {}; + + // Haversine formula to calculate distance between two points + function haversineDistance(lat1, lon1, lat2, lon2) { + const R = 6371000; // Earth radius in meters + const φ1 = lat1 * Math.PI / 180; + const φ2 = lat2 * Math.PI / 180; + const Δφ = (lat2 - lat1) * Math.PI / 180; + const Δλ = (lon2 - lon1) * Math.PI / 180; + + const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) + + Math.cos(φ1) * Math.cos(φ2) * + Math.sin(Δλ/2) * Math.sin(Δλ/2); + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + + return R * c; // Distance in meters + } + + // Extract coordinates and timestamps + let coordinates = []; + let timestamps = []; + + if (input.type === 'Feature' && input.geometry.type === 'LineString') { + coordinates = input.geometry.coordinates; + timestamps = input.properties?.timestamps || []; + } else if (input.type === 'LineString') { + coordinates = input.coordinates; + timestamps = input.properties?.timestamps || []; + } + + // Calculate total distance and time + let totalDistance = 0; + let totalTime = 0; + + for (let i = 1; i < coordinates.length && i < timestamps.length; i++) { + const coord1 = coordinates[i - 1]; + const coord2 = coordinates[i]; + const time1 = new Date(timestamps[i - 1]); + const time2 = new Date(timestamps[i]); + + // Calculate distance in meters + totalDistance += haversineDistance(coord1[1], coord1[0], coord2[1], coord2[0]); + + // Calculate time difference in seconds + totalTime += (time2 - time1) / 1000; + } + + if (totalTime === 0) { + return 0; + } + + // Calculate average speed based on requested unit + let averageSpeed = totalDistance / totalTime; // m/s + + if (time_unit === 'minutes') { + averageSpeed = averageSpeed / 60; // m/min + } else if (time_unit === 'hours') { + averageSpeed = averageSpeed / 3600; // m/h + } + + return averageSpeed; + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/statistics/speed-histogram.js b/examples/javascript-bundle/tools/statistics/speed-histogram.js new file mode 100644 index 0000000..f8cd357 --- /dev/null +++ b/examples/javascript-bundle/tools/statistics/speed-histogram.js @@ -0,0 +1,59 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.createSpeedHistogram = function(input, params) { + const { bins = 10, time_unit = 'seconds' } = params || {}; + + // First get the speed series using the calculateSpeedSeries tool + // This requires the speed-series tool to be loaded + if (!window.ToolVault.tools.calculateSpeedSeries) { + throw new Error('Speed series tool not available. Please load speed-series.js first.'); + } + + const speedSeries = window.ToolVault.tools.calculateSpeedSeries(input, { time_unit }); + + if (speedSeries.length === 0) { + return { + bins: [], + counts: [], + min: 0, + max: 0, + total: 0 + }; + } + + // Extract speeds + const speeds = speedSeries.map(s => s.speed); + const minSpeed = Math.min(...speeds); + const maxSpeed = Math.max(...speeds); + + // Create histogram bins + const binWidth = (maxSpeed - minSpeed) / bins; + const histogramBins = []; + const counts = []; + + for (let i = 0; i < bins; i++) { + const binStart = minSpeed + i * binWidth; + const binEnd = i === bins - 1 ? maxSpeed + 0.001 : binStart + binWidth; // Include max in last bin + + histogramBins.push({ + min: binStart, + max: binEnd, + center: (binStart + binEnd) / 2 + }); + + // Count speeds in this bin + const count = speeds.filter(speed => speed >= binStart && speed < binEnd).length; + counts.push(count); + } + + return { + bins: histogramBins, + counts: counts, + min: minSpeed, + max: maxSpeed, + total: speeds.length + }; + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/transform/flip-horizontal.js b/examples/javascript-bundle/tools/transform/flip-horizontal.js new file mode 100644 index 0000000..9860023 --- /dev/null +++ b/examples/javascript-bundle/tools/transform/flip-horizontal.js @@ -0,0 +1,99 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.flipHorizontal = function(input, params) { + const { axis = 'longitude' } = params || {}; + + // Deep clone the input + const result = JSON.parse(JSON.stringify(input)); + + // Find the center point for flipping + let center = 0; + let coordCount = 0; + let allCoords = []; + + // Helper to collect all coordinates + function collectCoordinates(coords, axisIndex) { + if (typeof coords[0] === 'number') { + center += coords[axisIndex]; + coordCount++; + allCoords.push(coords[axisIndex]); + } else { + coords.forEach(c => collectCoordinates(c, axisIndex)); + } + } + + // Helper to flip coordinates + function flipCoordinate(coord, axisIndex, centerValue) { + const newCoord = [...coord]; + newCoord[axisIndex] = 2 * centerValue - coord[axisIndex]; + return newCoord; + } + + // Helper to flip coordinates recursively + function flipCoordinates(coords, axisIndex, centerValue) { + if (typeof coords[0] === 'number') { + return flipCoordinate(coords, axisIndex, centerValue); + } + return coords.map(c => flipCoordinates(c, axisIndex, centerValue)); + } + + // Determine axis index (0 for longitude, 1 for latitude) + const axisIndex = axis === 'latitude' ? 1 : 0; + + // Collect all coordinates to find center + if (result.type === 'FeatureCollection') { + result.features.forEach(feature => { + if (feature.geometry && feature.geometry.coordinates) { + collectCoordinates(feature.geometry.coordinates, axisIndex); + } + }); + } else if (result.type === 'Feature') { + if (result.geometry && result.geometry.coordinates) { + collectCoordinates(result.geometry.coordinates, axisIndex); + } + } else if (result.coordinates) { + collectCoordinates(result.coordinates, axisIndex); + } + + // Calculate center + if (coordCount === 1) { + // For single points, flip around 0 + center = 0; + } else if (coordCount > 1) { + // For multiple points, use the bounding box center + const min = Math.min(...allCoords); + const max = Math.max(...allCoords); + center = (min + max) / 2; + } else { + center = 0; + } + + // Apply flipping + if (result.type === 'FeatureCollection') { + result.features = result.features.map(feature => { + if (feature.geometry && feature.geometry.coordinates) { + feature.geometry.coordinates = flipCoordinates( + feature.geometry.coordinates, + axisIndex, + center + ); + } + return feature; + }); + } else if (result.type === 'Feature') { + if (result.geometry && result.geometry.coordinates) { + result.geometry.coordinates = flipCoordinates( + result.geometry.coordinates, + axisIndex, + center + ); + } + } else if (result.coordinates) { + result.coordinates = flipCoordinates(result.coordinates, axisIndex, center); + } + + return result; + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/transform/flip-vertical.js b/examples/javascript-bundle/tools/transform/flip-vertical.js new file mode 100644 index 0000000..a76c8f3 --- /dev/null +++ b/examples/javascript-bundle/tools/transform/flip-vertical.js @@ -0,0 +1,10 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.flipVertical = function(input, params) { + // Flip vertical is essentially flip horizontal on latitude axis + const modifiedParams = { ...params, axis: 'latitude' }; + return window.ToolVault.tools.flipHorizontal(input, modifiedParams); + }; +})(); \ No newline at end of file diff --git a/examples/javascript-bundle/tools/transform/translate.js b/examples/javascript-bundle/tools/transform/translate.js new file mode 100644 index 0000000..111cbed --- /dev/null +++ b/examples/javascript-bundle/tools/transform/translate.js @@ -0,0 +1,70 @@ +(function() { + window.ToolVault = window.ToolVault || {}; + window.ToolVault.tools = window.ToolVault.tools || {}; + + window.ToolVault.tools.translate = function(input, params) { + // Handle null input gracefully + if (!input) { + return null; + } + + const { distance = 0, direction = 0, units = 'meters', dx = 0, dy = 0 } = params || {}; + + let deltaLon, deltaLat; + + // Support both dx/dy (simple offset) and distance/direction (polar) parameters + if (dx !== 0 || dy !== 0) { + // Simple dx/dy offset in degrees + deltaLon = dx; + deltaLat = dy; + } else { + // Convert distance to degrees (approximate) + // 1 degree latitude ≈ 111,320 meters + const metersPerDegreeLat = 111320; + let distanceInDegrees = distance / metersPerDegreeLat; + + if (units === 'kilometers') { + distanceInDegrees = (distance * 1000) / metersPerDegreeLat; + } + + // Convert direction to radians + const directionRad = (direction * Math.PI) / 180; + + // Calculate offset components + deltaLat = distanceInDegrees * Math.cos(directionRad); + deltaLon = distanceInDegrees * Math.sin(directionRad); + } + + // Deep clone the input + const result = JSON.parse(JSON.stringify(input)); + + // Helper function to translate coordinates + function translateCoordinates(coords) { + if (typeof coords[0] === 'number') { + // Single coordinate pair + return [coords[0] + deltaLon, coords[1] + deltaLat]; + } else { + // Array of coordinates + return coords.map(c => translateCoordinates(c)); + } + } + + // Process based on input type + if (result.type === 'FeatureCollection') { + result.features.forEach(feature => { + if (feature.geometry && feature.geometry.coordinates) { + feature.geometry.coordinates = translateCoordinates(feature.geometry.coordinates); + } + }); + } else if (result.type === 'Feature') { + if (result.geometry && result.geometry.coordinates) { + result.geometry.coordinates = translateCoordinates(result.geometry.coordinates); + } + } else if (result.type && result.coordinates) { + // Direct geometry + result.coordinates = translateCoordinates(result.coordinates); + } + + return result; + }; +})(); \ No newline at end of file From 2a1c0162253dbb8e9aeb4805129206db426f7df5 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 17:36:43 +0100 Subject: [PATCH 10/18] feat: update bundle schema to use git commits and temporal labels instead of categories --- examples/javascript-bundle/index.json | 84 ++++-- examples/javascript-bundle/index.schema.json | 258 +++++++++++++++++++ 2 files changed, 317 insertions(+), 25 deletions(-) create mode 100644 examples/javascript-bundle/index.schema.json diff --git a/examples/javascript-bundle/index.json b/examples/javascript-bundle/index.json index 1564185..543f319 100644 --- a/examples/javascript-bundle/index.json +++ b/examples/javascript-bundle/index.json @@ -1,6 +1,7 @@ { "name": "JavaScript Mock Tool Bundle", - "version": "0.1.0", + "commit": "f7g8h9i0", + "commit_date": "2024-01-31T16:45:00Z", "description": "A comprehensive collection of mock analysis tools for testing ToolVault's JavaScript toolset architecture", "author": "ToolVault Development Team", "license": "MIT", @@ -9,11 +10,14 @@ "tools": [ { "id": "translate", + "commit": "a1b2c3d4", + "commit_date": "2024-01-15T14:30:00Z", "name": "Translate Features", - "category": "transform", + "labels": ["transform", "geometry"], "description": "Translate geometric features by distance and direction or simple dx/dy offsets", - "input_types": ["Feature", "FeatureCollection", "geometry"], - "output_types": ["Feature", "FeatureCollection", "geometry"], + "input_types": ["Feature", "FeatureCollection", "Point", "LineString", "Polygon"], + "output_types": ["Feature", "FeatureCollection", "Point", "LineString", "Polygon"], + "isTemporal": false, "parameters": [ { "name": "distance", @@ -60,11 +64,14 @@ }, { "id": "flip-horizontal", + "commit": "b2c3d4e5", + "commit_date": "2024-01-15T10:15:00Z", "name": "Flip Horizontal", - "category": "transform", + "labels": ["transform", "geometry"], "description": "Flip features horizontally around the longitude or latitude axis", - "input_types": ["Feature", "FeatureCollection", "geometry"], - "output_types": ["Feature", "FeatureCollection", "geometry"], + "input_types": ["Feature", "FeatureCollection", "Point", "LineString", "Polygon"], + "output_types": ["Feature", "FeatureCollection", "Point", "LineString", "Polygon"], + "isTemporal": false, "parameters": [ { "name": "axis", @@ -83,11 +90,14 @@ }, { "id": "flip-vertical", + "commit": "c3d4e5f6", + "commit_date": "2024-01-15T10:15:00Z", "name": "Flip Vertical", - "category": "transform", + "labels": ["transform", "geometry"], "description": "Flip features vertically around the latitude axis", - "input_types": ["Feature", "FeatureCollection", "geometry"], - "output_types": ["Feature", "FeatureCollection", "geometry"], + "input_types": ["Feature", "FeatureCollection", "Point", "LineString", "Polygon"], + "output_types": ["Feature", "FeatureCollection", "Point", "LineString", "Polygon"], + "isTemporal": false, "parameters": [], "examples": [ { @@ -98,11 +108,14 @@ }, { "id": "speed-series", + "commit": "d4e5f6g7", + "commit_date": "2024-01-18T09:45:00Z", "name": "Calculate Speed Series", - "category": "analysis", + "labels": ["analysis", "temporal"], "description": "Calculate speed time series from GPS track with timestamps", - "input_types": ["Feature"], + "input_types": ["LineString"], "output_types": ["array"], + "isTemporal": true, "parameters": [ { "name": "time_unit", @@ -137,11 +150,14 @@ }, { "id": "direction-series", + "commit": "e5f6g7h8", + "commit_date": "2024-01-20T11:20:00Z", "name": "Calculate Direction Series", - "category": "analysis", + "labels": ["analysis", "temporal"], "description": "Calculate bearing/direction time series from GPS track", - "input_types": ["Feature"], + "input_types": ["LineString"], "output_types": ["array"], + "isTemporal": true, "parameters": [ { "name": "smoothing", @@ -169,11 +185,14 @@ }, { "id": "average-speed", + "commit": "f6g7h8i9", + "commit_date": "2024-01-22T16:30:00Z", "name": "Calculate Average Speed", - "category": "statistics", + "labels": ["statistics", "temporal"], "description": "Calculate the average speed over an entire GPS track", - "input_types": ["Feature"], + "input_types": ["LineString"], "output_types": ["number"], + "isTemporal": true, "parameters": [ { "name": "time_unit", @@ -196,11 +215,14 @@ }, { "id": "speed-histogram", + "commit": "g7h8i9j0", + "commit_date": "2024-01-25T13:15:00Z", "name": "Create Speed Histogram", - "category": "statistics", + "labels": ["statistics", "temporal"], "description": "Generate a histogram of speeds from GPS track data", - "input_types": ["Feature"], + "input_types": ["LineString"], "output_types": ["object"], + "isTemporal": true, "parameters": [ { "name": "bins", @@ -229,11 +251,14 @@ }, { "id": "smooth-polyline", + "commit": "h8i9j0k1", + "commit_date": "2024-01-28T10:00:00Z", "name": "Smooth Polyline", - "category": "processing", + "labels": ["processing", "geometry"], "description": "Apply smoothing algorithms to LineString geometries", - "input_types": ["Feature", "FeatureCollection", "geometry"], - "output_types": ["Feature", "FeatureCollection", "geometry"], + "input_types": ["Feature", "FeatureCollection", "LineString"], + "output_types": ["Feature", "FeatureCollection", "LineString"], + "isTemporal": false, "parameters": [ { "name": "algorithm", @@ -262,11 +287,14 @@ }, { "id": "export-csv", + "commit": "i9j0k1l2", + "commit_date": "2024-01-30T14:45:00Z", "name": "Export to CSV", - "category": "io", + "labels": ["io", "export"], "description": "Export GeoJSON features to CSV format with coordinates and properties", - "input_types": ["Feature", "FeatureCollection", "geometry"], + "input_types": ["Feature", "FeatureCollection", "Point", "LineString", "Polygon"], "output_types": ["string"], + "isTemporal": false, "parameters": [ { "name": "include_properties", @@ -301,11 +329,14 @@ }, { "id": "export-rep", + "commit": "j0k1l2m3", + "commit_date": "2024-01-31T08:30:00Z", "name": "Export to REP Format", - "category": "io", + "labels": ["io", "export", "maritime"], "description": "Export GeoJSON features to Debrief REP format for vessel tracking", "input_types": ["Feature", "FeatureCollection"], "output_types": ["string"], + "isTemporal": false, "parameters": [ { "name": "precision", @@ -327,11 +358,14 @@ }, { "id": "import-rep", + "commit": "k1l2m3n4", + "commit_date": "2024-01-31T12:00:00Z", "name": "Import REP Format", - "category": "io", + "labels": ["io", "import", "maritime"], "description": "Import Debrief REP format files into GeoJSON FeatureCollection", "input_types": ["string"], "output_types": ["FeatureCollection"], + "isTemporal": false, "parameters": [ { "name": "encoding", diff --git a/examples/javascript-bundle/index.schema.json b/examples/javascript-bundle/index.schema.json new file mode 100644 index 0000000..a00d181 --- /dev/null +++ b/examples/javascript-bundle/index.schema.json @@ -0,0 +1,258 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://toolvault.org/schema/bundle-index/v1.0.0", + "title": "ToolVault Bundle Index Schema", + "description": "JSON Schema for ToolVault bundle index.json files", + "type": "object", + "required": [ + "name", + "commit", + "commit_date", + "description", + "bundle_format", + "runtime", + "tools" + ], + "properties": { + "name": { + "type": "string", + "description": "Human-readable name of the tool bundle" + }, + "commit": { + "type": "string", + "pattern": "^[a-f0-9]{8,40}$", + "description": "Git commit hash when bundle was last modified" + }, + "commit_date": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp of the bundle commit" + }, + "description": { + "type": "string", + "description": "Detailed description of the bundle's purpose and contents" + }, + "author": { + "type": "string", + "description": "Author or organization that created the bundle" + }, + "license": { + "type": "string", + "description": "Software license (SPDX identifier preferred)" + }, + "bundle_format": { + "type": "string", + "enum": ["javascript", "python", "r", "executable"], + "description": "Format/language of the tools in this bundle" + }, + "runtime": { + "type": "string", + "enum": ["browser", "node", "python", "r", "system"], + "description": "Runtime environment required to execute tools" + }, + "tools": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/tool" + }, + "description": "Array of tools included in this bundle" + }, + "dependencies": { + "type": "object", + "description": "Bundle-level dependencies and requirements" + }, + "test_coverage": { + "type": "object", + "properties": { + "statements": { + "type": "string", + "pattern": "^\\d+(\\.\\d+)?%$", + "description": "Statement coverage percentage" + }, + "branches": { + "type": "string", + "pattern": "^\\d+(\\.\\d+)?%$", + "description": "Branch coverage percentage" + }, + "functions": { + "type": "string", + "pattern": "^\\d+(\\.\\d+)?%$", + "description": "Function coverage percentage" + }, + "lines": { + "type": "string", + "pattern": "^\\d+(\\.\\d+)?%$", + "description": "Line coverage percentage" + } + }, + "description": "Test coverage statistics for the bundle" + }, + "bundle_size": { + "type": "string", + "description": "Approximate bundle size (e.g., '~25KB')" + }, + "created": { + "type": "string", + "format": "date", + "description": "Date the bundle was created" + }, + "last_updated": { + "type": "string", + "format": "date", + "description": "Date the bundle was last updated" + } + }, + "additionalProperties": false, + "$defs": { + "tool": { + "type": "object", + "required": [ + "id", + "commit", + "commit_date", + "name", + "labels", + "description", + "input_types", + "output_types", + "isTemporal", + "parameters" + ], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z0-9-]+$", + "description": "Unique identifier for the tool (lowercase, hyphens allowed)" + }, + "commit": { + "type": "string", + "pattern": "^[a-f0-9]{8,40}$", + "description": "Git commit hash when tool was last modified" + }, + "commit_date": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp of the commit" + }, + "name": { + "type": "string", + "description": "Human-readable name of the tool" + }, + "labels": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "transform", "analysis", "statistics", "processing", "io", + "geometry", "temporal", "export", "import", "maritime", + "gis", "spatial", "visualization" + ] + }, + "minItems": 1, + "uniqueItems": true, + "description": "Categorization labels for the tool" + }, + "description": { + "type": "string", + "description": "Detailed description of what the tool does" + }, + "input_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "Feature", "FeatureCollection", + "Point", "LineString", "Polygon", + "string", "number", "array", "object" + ] + }, + "minItems": 1, + "uniqueItems": true, + "description": "Types of data this tool can accept as input" + }, + "output_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "Feature", "FeatureCollection", + "Point", "LineString", "Polygon", + "string", "number", "array", "object" + ] + }, + "minItems": 1, + "uniqueItems": true, + "description": "Types of data this tool produces as output" + }, + "isTemporal": { + "type": "boolean", + "description": "Whether this tool requires temporal data (timestamps)" + }, + "parameters": { + "type": "array", + "items": { + "$ref": "#/$defs/parameter" + }, + "description": "Parameters accepted by this tool" + }, + "examples": { + "type": "array", + "items": { + "$ref": "#/$defs/example" + }, + "description": "Usage examples for this tool" + } + }, + "additionalProperties": false + }, + "parameter": { + "type": "object", + "required": ["name", "type", "description"], + "properties": { + "name": { + "type": "string", + "pattern": "^[a-zA-Z_][a-zA-Z0-9_]*$", + "description": "Parameter name (valid identifier)" + }, + "type": { + "type": "string", + "enum": ["string", "number", "boolean", "array", "object"], + "description": "JavaScript type of the parameter" + }, + "default": { + "description": "Default value for the parameter (type should match 'type' field)" + }, + "enum": { + "type": "array", + "minItems": 1, + "description": "Allowed values for the parameter (for string/number types)" + }, + "description": { + "type": "string", + "description": "Human-readable description of the parameter" + } + }, + "additionalProperties": false + }, + "example": { + "type": "object", + "required": ["name", "parameters"], + "properties": { + "name": { + "type": "string", + "description": "Name of this example" + }, + "parameters": { + "type": "object", + "description": "Parameter values for this example" + }, + "description": { + "type": "string", + "description": "Optional description of what this example demonstrates" + } + }, + "additionalProperties": false + } + } +} \ No newline at end of file From 266c9b7e51d4ec9881f2779ecca18a52bbd2215a Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 17:42:51 +0100 Subject: [PATCH 11/18] retire some requirements --- docs/software_requirements.md | 45 +++++------------------------------ 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/docs/software_requirements.md b/docs/software_requirements.md index b894183..7df844f 100644 --- a/docs/software_requirements.md +++ b/docs/software_requirements.md @@ -55,41 +55,7 @@ ToolVault SHALL provide a metadata-driven interface for tool discovery, executio ### 2.3 Tool Execution -**FR-3.1** The system SHALL execute JavaScript/TypeScript tools in isolated Web Worker contexts. - -**FR-3.2** The system SHALL support tool execution with: -- Timeout protection (configurable per tool) -- Memory usage monitoring -- Cancellation capability -- Progress reporting - -**FR-3.3** The system SHALL maintain execution state: -- Pending -- Running -- Completed -- Failed -- Cancelled - -**FR-3.4** The system SHALL capture execution metrics: -- Start and end timestamps -- Duration -- Resource usage -- Error information if applicable - -### 2.4 Pipeline Management - -**FR-4.1** The system SHALL automatically capture tool execution sequences as pipelines. - -**FR-4.2** The system SHALL allow users to: -- View pipeline as a sequence of steps -- Enable/disable individual steps -- Reorder steps -- Modify parameters for each step -- Save pipelines as reusable templates - -**FR-4.3** The system SHALL support branching pipelines for comparison workflows. - -**FR-4.4** The system SHALL allow grouping of steps into composite operations. +**FR-3.1** The system SHALL execute JavaScript/TypeScript tools in the current context. Note: in the future we may switch to web workers (including timeout and memory protection, and cancellation), but not now. ### 2.5 Provenance Tracking @@ -114,7 +80,8 @@ ToolVault SHALL provide a metadata-driven interface for tool discovery, executio **FR-6.1** The system SHALL support dual-mode execution: - Original version (for reproducibility) -- Latest version (for comparison) +- Latest compatible version (to use up-to-date features) +- Latest version (for comparison). Fail if not compatible with params. **FR-6.2** The system SHALL track tool versions using semantic versioning. @@ -124,7 +91,7 @@ ToolVault SHALL provide a metadata-driven interface for tool discovery, executio ### 2.7 Output Management -**FR-7.1** The system SHALL automatically detect output types and select appropriate renderers: +**FR-7.1** The system SHALL automatically detect input and output types and select appropriate renderers, though the user may override this: - JSON (tree view with syntax highlighting) - Table (sortable, filterable grid) - GeoJSON (interactive map using LeafletJS) @@ -221,7 +188,7 @@ ToolVault SHALL provide a metadata-driven interface for tool discovery, executio ### 3.6 Security -**NFR-6.1** The system SHALL execute tools in isolated contexts (Web Workers). +**NFR-6.1** The system MAY execute tools in isolated contexts (Web Workers). **NFR-6.2** The system SHALL sanitize all HTML output before rendering. @@ -389,7 +356,7 @@ ToolVault SHALL provide a metadata-driven interface for tool discovery, executio - Browser-based execution only (no server-side processing in initial phases) - Maximum bundle size of 50MB -- Web Worker API required for tool execution +- Web Worker API required for tool execution (in the future) - ES2020+ JavaScript features required ### 7.2 Regulatory Constraints From 56d83aad24f31a0071183c5c26f6aed6fc786457 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Tue, 19 Aug 2025 17:43:02 +0100 Subject: [PATCH 12/18] feat: add commit history data for javascript bundle tools --- examples/javascript-bundle/history.json | 332 ++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 examples/javascript-bundle/history.json diff --git a/examples/javascript-bundle/history.json b/examples/javascript-bundle/history.json new file mode 100644 index 0000000..b1d9e2d --- /dev/null +++ b/examples/javascript-bundle/history.json @@ -0,0 +1,332 @@ +{ + "translate": [ + { + "commit": "a1b2c3d4", + "commit_date": "2024-01-15T14:30:00Z", + "message": "Fix coordinate drift in hyperspace jumps - Imperial tracking was exploiting precision errors in our fleet repositioning calculations", + "author": "Luke Skywalker ", + "changes": ["Fixed floating point precision in distance calculations", "Added coordinate validation for extreme distances"] + }, + { + "commit": "5e6f7g8h", + "commit_date": "2022-11-08T09:15:00Z", + "message": "Add polar coordinate support for Death Star approach vectors - Cartesian offsets weren't sufficient for orbital mechanics", + "author": "Wedge Antilles ", + "changes": ["Implemented distance/direction parameter support", "Added proper spherical coordinate handling"] + }, + { + "commit": "9i0j1k2l", + "commit_date": "2021-06-22T16:45:00Z", + "message": "Emergency patch: prevent translation beyond sensor range - lost 3 probe droids to coordinate overflow", + "author": "Mon Mothma ", + "changes": ["Added bounds checking for translation distances", "Fixed coordinate overflow in large translations"] + }, + { + "commit": "3m4n5o6p", + "commit_date": "2020-03-14T11:20:00Z", + "message": "Initial implementation for rebel fleet positioning - need to stay mobile to avoid Imperial patrols", + "author": "Princess Leia ", + "changes": ["Basic geometric translation functionality", "Support for Feature and FeatureCollection inputs"] + } + ], + "flip-horizontal": [ + { + "commit": "b2c3d4e5", + "commit_date": "2024-01-15T10:15:00Z", + "message": "Optimize reflection calculations for Star Destroyer evasion maneuvers - every microsecond counts when dodging turbolaser fire", + "author": "Han Solo ", + "changes": ["Improved bounding box center calculation", "Fixed edge case for single-point geometries"] + }, + { + "commit": "7q8r9s0t", + "commit_date": "2023-05-30T13:22:00Z", + "message": "Add latitude axis flipping for Death Star trench runs - original tool only worked for longitude, useless for polar approaches", + "author": "Biggs Darklighter ", + "changes": ["Added axis parameter with longitude/latitude options", "Generalized flipping algorithm for both axes"] + }, + { + "commit": "1u2v3w4x", + "commit_date": "2022-02-17T08:30:00Z", + "message": "Fix mirror point calculation - Imperial analysts figured out our fleet movements were perfectly predictable", + "author": "Admiral Ackbar ", + "changes": ["Corrected center point calculation for multi-geometry features", "Added proper coordinate transformation matrix"] + }, + { + "commit": "5y6z7a8b", + "commit_date": "2019-12-03T19:45:00Z", + "message": "Create horizontal flip tool for confusing Imperial tracking sensors - mirror our formations to throw off their predictions", + "author": "Jyn Erso ", + "changes": ["Initial horizontal reflection implementation", "Basic geometry support for Point and LineString"] + } + ], + "flip-vertical": [ + { + "commit": "c3d4e5f6", + "commit_date": "2024-01-15T10:15:00Z", + "message": "Simplify vertical flip to delegate to horizontal flip - code duplication was making maintenance difficult during the siege of Hoth", + "author": "C-3PO ", + "changes": ["Refactored to use flip-horizontal with latitude axis", "Reduced code duplication and maintenance overhead"] + }, + { + "commit": "9c0d1e2f", + "commit_date": "2023-04-12T14:18:00Z", + "message": "Fix inverted latitude calculations - our Y-wing formations were flying upside down relative to target coordinates", + "author": "Gold Leader ", + "changes": ["Corrected latitude coordinate inversion logic", "Added validation for proper north/south orientation"] + }, + { + "commit": "3g4h5i6j", + "commit_date": "2021-09-25T12:33:00Z", + "message": "Add vertical flip capability for Imperial formation mimicry - they expect us to mirror horizontally, not vertically", + "author": "Cassian Andor ", + "changes": ["Implemented vertical reflection around equatorial plane", "Support for flipping Y-coordinates of geometric features"] + } + ], + "speed-series": [ + { + "commit": "d4e5f6g7", + "commit_date": "2024-01-18T16:20:00Z", + "message": "Critical fix: timestamp location bug causing hyperspace miscalculations - ships were arriving days early or late", + "author": "R2-D2 ", + "changes": ["Fixed timestamps property location in GeoJSON features", "Corrected time unit conversion calculations"] + }, + { + "commit": "7k8l9m0n", + "commit_date": "2023-08-14T11:45:00Z", + "message": "Add smoothing option to filter out sensor noise from Imperial jamming - raw speed data was too erratic for navigation", + "author": "Poe Dameron ", + "changes": ["Implemented moving average smoothing algorithm", "Added configurable window size parameter"] + }, + { + "commit": "1o2p3q4r", + "commit_date": "2022-07-09T09:12:00Z", + "message": "Support multiple time units - rebel pilots think in different scales than Imperial standardization", + "author": "Hera Syndulla ", + "changes": ["Added seconds, minutes, hours time unit options", "Proper unit conversion for speed calculations"] + }, + { + "commit": "5s6t7u8v", + "commit_date": "2021-01-28T15:33:00Z", + "message": "Implement speed analysis for tracking TIE fighter patrol patterns - need to predict their intercept courses", + "author": "Kanan Jarrus ", + "changes": ["Core speed calculation from GPS tracks", "Haversine distance formula for accurate measurements"] + }, + { + "commit": "9w0x1y2z", + "commit_date": "2019-10-15T13:27:00Z", + "message": "Initial speed series tool for rebel fleet coordination - essential for synchronized hyperspace exits", + "author": "Ezra Bridger ", + "changes": ["Basic time series speed calculation", "Support for timestamped GPS track analysis"] + } + ], + "direction-series": [ + { + "commit": "e5f6g7h8", + "commit_date": "2024-01-20T12:10:00Z", + "message": "Handle edge case for stationary targets - direction calculations were failing on Imperial listening posts", + "author": "Sabine Wren ", + "changes": ["Fixed division by zero in bearing calculations", "Added handling for zero-distance coordinate pairs"] + }, + { + "commit": "3a4b5c6d", + "commit_date": "2023-06-18T17:25:00Z", + "message": "Add circular smoothing for bearing data - regular smoothing was causing 359° to 1° transitions to break our autopilots", + "author": "Wedge Antilles ", + "changes": ["Implemented circular mean for angular smoothing", "Fixed bearing discontinuity handling at 0°/360°"] + }, + { + "commit": "7e8f9g0h", + "commit_date": "2022-03-11T14:52:00Z", + "message": "Correct bearing calculations for Imperial formation tracking - our intercept angles were consistently off by 180°", + "author": "Garven Dreis ", + "changes": ["Fixed atan2 parameter order in bearing calculation", "Corrected coordinate system orientation"] + }, + { + "commit": "1i2j3k4l", + "commit_date": "2020-11-07T10:18:00Z", + "message": "Create direction analysis tool for predicting Imperial patrol routes - knowing their heading patterns is crucial", + "author": "General Dodonna ", + "changes": ["Implemented bearing calculation from coordinate sequences", "Added true north bearing orientation"] + } + ], + "average-speed": [ + { + "commit": "f6g7h8i9", + "commit_date": "2024-01-22T13:45:00Z", + "message": "Handle zero-time intervals gracefully - Imperial chronometer jamming was causing division by zero crashes", + "author": "General Rieekan ", + "changes": ["Added zero time interval detection", "Return zero speed for invalid time sequences"] + }, + { + "commit": "5m6n7o8p", + "commit_date": "2023-09-03T08:30:00Z", + "message": "Optimize calculation for long Imperial patrol routes - processing 12-hour Star Destroyer tracks was too slow", + "author": "Commander Sato ", + "changes": ["Improved algorithm efficiency for large datasets", "Added early exit for single-point tracks"] + }, + { + "commit": "9q0r1s2t", + "commit_date": "2022-04-26T16:15:00Z", + "message": "Fix time unit conversion bug - our interceptor speeds were calculated as impossibly fast, confused our pilots", + "author": "General Merrick ", + "changes": ["Corrected division vs multiplication in unit conversions", "Added validation for reasonable speed values"] + }, + { + "commit": "3u4v5w6x", + "commit_date": "2021-08-19T11:22:00Z", + "message": "Add average speed calculation for Imperial fleet analysis - need baseline metrics for identifying ship classes", + "author": "Admiral Raddus ", + "changes": ["Implemented total distance over total time calculation", "Support for multiple time unit outputs"] + } + ], + "speed-histogram": [ + { + "commit": "g7h8i9j0", + "commit_date": "2024-01-25T09:30:00Z", + "message": "Fix dependency on speed-series tool - histogram was failing when speed calculation module wasn't loaded first", + "author": "Jyn Erso ", + "changes": ["Added proper error handling for missing dependencies", "Improved tool loading validation"] + }, + { + "commit": "1y2z3a4b", + "commit_date": "2023-12-08T14:20:00Z", + "message": "Add configurable bin count - default 10 bins wasn't granular enough to distinguish between different TIE fighter variants", + "author": "Nien Nunb ", + "changes": ["Made bin count configurable via parameters", "Added validation for minimum bin requirements"] + }, + { + "commit": "5c6d7e8f", + "commit_date": "2022-10-14T12:45:00Z", + "message": "Implement speed distribution analysis for identifying Imperial ship signatures - each class has distinct speed profiles", + "author": "Captain Antilles ", + "changes": ["Core histogram generation from speed data", "Statistical binning with min/max/count tracking"] + } + ], + "smooth-polyline": [ + { + "commit": "h8i9j0k1", + "commit_date": "2024-01-28T15:50:00Z", + "message": "Add moving average algorithm - Gaussian smoothing was over-correcting our hyperspace exit coordinates", + "author": "Lando Calrissian ", + "changes": ["Implemented moving average smoothing option", "Added algorithm parameter for user selection"] + }, + { + "commit": "9g0h1i2j", + "commit_date": "2023-07-22T11:33:00Z", + "message": "Fix coordinate preservation at endpoints - smoothing was causing our ships to overshoot landing pad boundaries", + "author": "Chewbacca ", + "changes": ["Preserve original endpoint coordinates", "Improved edge handling in smoothing algorithms"] + }, + { + "commit": "3k4l5m6n", + "commit_date": "2022-12-01T16:28:00Z", + "message": "Add Gaussian smoothing for cleaning up sensor tracks - Imperial jamming creates too much noise in our navigation data", + "author": "Admiral Holdo ", + "changes": ["Implemented Gaussian kernel smoothing", "Configurable window size for smoothing intensity"] + }, + { + "commit": "7o8p9q0r", + "commit_date": "2020-05-17T13:11:00Z", + "message": "Create polyline smoothing tool for cleaning hyperspace route calculations - raw astrogation data too jittery for safe jumps", + "author": "General Cracken ", + "changes": ["Basic coordinate smoothing for LineString geometries", "Support for Feature and FeatureCollection inputs"] + } + ], + "export-csv": [ + { + "commit": "i9j0k1l2", + "commit_date": "2024-01-30T11:25:00Z", + "message": "Add custom separator support - Imperial data formats use semicolons, need compatibility for infiltration missions", + "author": "Kyle Katarn ", + "changes": ["Configurable field separator parameter", "Proper escaping for special characters in separators"] + }, + { + "commit": "5s6t7u8v", + "commit_date": "2023-11-16T09:42:00Z", + "message": "Fix timestamp handling for coordinate sequences - each position needs its own timestamp for Imperial patrol analysis", + "author": "Jan Dodonna ", + "changes": ["Individual timestamp per coordinate pair", "Proper handling of mismatched timestamp arrays"] + }, + { + "commit": "9w0x1y2z", + "commit_date": "2022-08-23T14:17:00Z", + "message": "Add property inclusion control - sometimes we need to hide rebel ship identifiers when exporting data", + "author": "Bail Organa ", + "changes": ["Configurable property inclusion/exclusion", "Clean coordinate-only export option"] + }, + { + "commit": "3a4b5c6d", + "commit_date": "2021-04-09T10:55:00Z", + "message": "Implement CSV export for sharing navigation data with New Republic fleet - standardized format for interoperability", + "author": "General Cracken ", + "changes": ["Basic GeoJSON to CSV conversion", "Support for Point, LineString, and Polygon geometries"] + } + ], + "export-rep": [ + { + "commit": "j0k1l2m3", + "commit_date": "2024-01-31T14:18:00Z", + "message": "Fix vessel name handling with special characters - some rebel ship names have hyphens that were breaking the format", + "author": "Admiral Ackbar ", + "changes": ["Improved vessel name parsing and validation", "Proper handling of quoted names with special characters"] + }, + { + "commit": "7e8f9g0h", + "commit_date": "2023-10-05T16:30:00Z", + "message": "Add support for heading, speed, depth data - Imperial naval tracking requires full kinematic information", + "author": "Commander Sato ", + "changes": ["Extended REP format with kinematic parameters", "Support for per-coordinate heading/speed/depth values"] + }, + { + "commit": "1i2j3k4l", + "commit_date": "2022-06-11T13:22:00Z", + "message": "Implement DMS coordinate conversion - Imperial navigation uses degrees/minutes/seconds, not decimal degrees", + "author": "Captain Antilles ", + "changes": ["Accurate DMS coordinate conversion", "Proper hemisphere handling for N/S/E/W indicators"] + }, + { + "commit": "5m6n7o8p", + "commit_date": "2020-09-28T12:08:00Z", + "message": "Create REP format exporter for compatibility with Imperial Debrief systems - need to infiltrate their tracking networks", + "author": "Cassian Andor ", + "changes": ["Initial REP format implementation", "Basic vessel tracking with timestamps"] + } + ], + "import-rep": [ + { + "commit": "k1l2m3n4", + "commit_date": "2024-01-31T14:18:00Z", + "message": "Fix quote parsing for vessel names - Imperial ship names like 'Executor Class' were being truncated", + "author": "General Dodonna ", + "changes": ["Simplified quote handling in vessel name parsing", "Robust parsing for names with embedded spaces"] + }, + { + "commit": "9q0r1s2t", + "commit_date": "2023-12-20T10:44:00Z", + "message": "Add support for multiple vessel tracks - captured Imperial data files contain entire fleet movements", + "author": "Mon Mothma ", + "changes": ["Multi-vessel track separation by name", "Proper GeoJSON FeatureCollection generation"] + }, + { + "commit": "3u4v5w6x", + "commit_date": "2022-09-15T15:25:00Z", + "message": "Fix timestamp parsing for fractional seconds - Imperial chronometers have microsecond precision we were losing", + "author": "C-3PO ", + "changes": ["Proper millisecond handling in timestamp parsing", "UTC timezone conversion for accurate timing"] + }, + { + "commit": "7y8z9a0b", + "commit_date": "2021-03-06T11:17:00Z", + "message": "Implement REP format parser for analyzing captured Imperial tracking data - their Debrief files are treasure troves", + "author": "Princess Leia ", + "changes": ["Core REP format parsing with DMS coordinates", "Comment line filtering and error handling"] + }, + { + "commit": "1c2d3e4f", + "commit_date": "2019-11-22T08:33:00Z", + "message": "Initial REP import capability for intelligence gathering - need to decode Imperial fleet movement patterns", + "author": "General Rieekan ", + "changes": ["Basic REP file structure recognition", "Timestamp and coordinate extraction"] + } + ] +} \ No newline at end of file From 878dc33e8e9e8fe409b6d4cbe74cb800133b76aa Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Wed, 20 Aug 2025 09:03:15 +0100 Subject: [PATCH 13/18] first pass at Task 1 TAPS --- .../Task_1.1_React_TypeScript_SPA_Setup.md | 71 ++++++++++ ..._1.2_Load_JavaScript_Bundle_Integration.md | 74 +++++++++++ .../Task_1.3_Metadata_Driven_UI_Generation.md | 84 ++++++++++++ .../Task_1.4_Tool_Discovery_and_Browsing.md | 98 ++++++++++++++ .../Task_1.5_Tool_Execution_Interface.md | 88 +++++++++++++ .../tasks/Task_1.6_GitHub_Pages_Deployment.md | 124 ++++++++++++++++++ 6 files changed, 539 insertions(+) create mode 100644 prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md create mode 100644 prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md create mode 100644 prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md create mode 100644 prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md create mode 100644 prompts/tasks/Task_1.5_Tool_Execution_Interface.md create mode 100644 prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md diff --git a/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md b/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md new file mode 100644 index 0000000..6e06bcb --- /dev/null +++ b/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md @@ -0,0 +1,71 @@ +# APM Task Assignment: React/TypeScript SPA Setup + +## 1. Agent Role & APM Context + +You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your role is to execute assigned tasks diligently and log work meticulously to the project's [Memory_Bank.md](../../Memory_Bank.md) file. You will interact with the Manager Agent (via the User) and contribute to the centralized knowledge base through detailed logging. + +## 2. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.1` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Establish the core frontend application infrastructure using modern web development tools to support the JavaScript tool bundle created in Phase 0. + +**Detailed Action Steps:** + +1. **Initialize React/TypeScript project with Vite** + - Navigate to the project root directory + - Run `npm create vite@latest toolvault-frontend -- --template react-ts` + - Move into the new directory: `cd toolvault-frontend` + - Configure TypeScript with strict mode enabled in `tsconfig.json` + - Set up project directory structure: + - `/src/components` - React components + - `/src/services` - API and tool loading services + - `/src/types` - TypeScript interface definitions + - `/src/utils` - Utility functions + - Configure Vite for development and production builds with proper base path settings + +2. **Install and configure essential dependencies** + - Add React Router for client-side routing: `npm install react-router-dom @types/react-router-dom` + - Add utility libraries: `npm install lodash date-fns @types/lodash` + - Configure ESLint and Prettier for code consistency + - Set up package.json scripts for development workflow + +3. **Set up development environment** + - Create `.vscode/settings.json` and `.vscode/extensions.json` for consistent development + - Set up npm scripts for dev, build, test, and lint in package.json + - Create basic folder structure and initial components: + - `src/App.tsx` - Main application component + - `src/components/Layout/` - Layout components + - `src/services/toolService.ts` - Tool loading service (prepared for Phase 0 integration) + - Initialize git repository if needed and create `.gitignore` + +**Provide Necessary Context/Assets:** +- The application must be designed to integrate with the JavaScript tool bundle from `examples/javascript-bundle/` +- Reference `examples/javascript-bundle/index.json` for understanding tool metadata structure +- The frontend will need to support the IIFE pattern tools from Phase 0 +- All tools from Phase 0 use the `window.ToolVault.tools` namespace + +## 3. Expected Output & Deliverables + +**Define Success:** A fully functional React/TypeScript application with Vite build system that can serve as the foundation for integrating Phase 0 JavaScript tools. + +**Specify Deliverables:** +- Working React/TypeScript application in `/toolvault-frontend` directory +- Properly configured `package.json` with all required dependencies +- TypeScript configuration with strict mode enabled +- ESLint and Prettier configuration files +- Basic component structure and routing setup +- Development scripts that run without errors: `npm run dev`, `npm run build`, `npm run lint` + +## 4. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.1 in the Implementation Plan +- A clear description of the React/TypeScript setup actions taken +- Key configuration decisions made (TypeScript settings, dependencies chosen) +- Any challenges encountered during setup +- Confirmation of successful execution (dev server running, build succeeds) + +## 5. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md b/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md new file mode 100644 index 0000000..07683e1 --- /dev/null +++ b/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md @@ -0,0 +1,74 @@ +# APM Task Assignment: Load Standard Bundle from Phase 0 + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.2` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Integrate the JavaScript toolbox created in Phase 0 as the standard tool bundle for the React frontend. + +**Detailed Action Steps:** + +1. **Create bundle loading service** + - Reference `/examples/javascript-bundle/index.json` as the primary bundle source + - Implement `src/services/bundleLoader.ts` to fetch and parse tool metadata from Phase 0 + - Create TypeScript interfaces in `src/types/tools.ts` matching Phase 0 tool definitions: + - Interface for tool metadata structure from `index.json` + - Parameter schema interfaces (number, string, boolean, enum types) + - Input/output type definitions (Feature, FeatureCollection, etc.) + - Handle loading errors with fallback mechanisms and user-friendly error messages + - Support both development (local file) and production (bundled) loading scenarios + +2. **Implement tool registry system** + - Create `src/services/toolRegistry.ts` for in-memory registry of Phase 0 bundle tools + - Implement filtering capabilities by: + - Category (Transform, Analysis, Statistics, Processing, I/O - note: these should not be hard-coded, but derivte from the metadata) + - Runtime type ("javascript") + - Search terms across tool names and descriptions + - Implement tool metadata caching for performance optimization + - Add tool availability status tracking and health checks + - Create methods for: `getTools()`, `getToolById()`, `getToolsByCategory()`, `searchTools()` + +3. **Load Phase 0 JavaScript tools** + - Implement dynamic script loading in `src/services/scriptLoader.ts` using IIFE pattern from Phase 0 + - Verify tool function availability in `window.ToolVault.tools` namespace after loading + - Handle script loading errors gracefully with retry mechanisms + - Test integration with all 12 tools from Phase 0: + - Transform: translate, flip-horizontal, flip-vertical + - Analysis: speed-series, direction-series + - Statistics: average-speed, speed-histogram + - Processing: smooth-polyline + - I/O: import-rep, export-rep, export-csv + - Create tool execution wrapper with error handling and validation + +**Provide Necessary Context/Assets:** +- Phase 0 tools are implemented using IIFE pattern and register in `window.ToolVault.tools` namespace +- The `examples/javascript-bundle/index.json` contains complete metadata with runtime field = "javascript" +- All Phase 0 tools expect specific input formats: GeoJSON Feature/FeatureCollection, LineString, etc. +- Phase 0 tools include comprehensive parameter schemas with defaults and validation rules +- Reference ADR-013 for JavaScript tool implementation specification + +## 2. Expected Output & Deliverables + +**Define Success:** A fully functional bundle loading system that can dynamically load and execute all Phase 0 JavaScript tools from the frontend. + +**Specify Deliverables:** +- `src/services/bundleLoader.ts` - Bundle metadata loading service +- `src/services/toolRegistry.ts` - Tool registry and filtering system +- `src/services/scriptLoader.ts` - Dynamic script loading for IIFE tools +- `src/types/tools.ts` - TypeScript interfaces for Phase 0 tool structure +- Successful loading and execution test of all 12 Phase 0 tools +- Error handling for bundle loading failures +- Tool availability verification system + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.2 in the Implementation Plan +- A clear description of the bundle integration implementation +- Key decisions made for tool loading architecture +- Any challenges encountered with Phase 0 tool integration +- Confirmation of successful execution (all 12 Phase 0 tools loading and executing correctly) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md b/prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md new file mode 100644 index 0000000..c2299d8 --- /dev/null +++ b/prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md @@ -0,0 +1,84 @@ +# APM Task Assignment: Metadata-Driven UI from index.json + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.3` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Create dynamic UI components that generate interfaces directly from Phase 0 tool metadata, following the metadata-driven UI architecture (ADR-001). + +**Detailed Action Steps:** + +1. **Parse tool metadata from Phase 0 index.json** + - Load and validate `examples/javascript-bundle/index.json` structure with runtime field + - Create comprehensive TypeScript interfaces in `src/types/` for: + - Tool definitions with parameter schemas + - Parameter types: number, string, boolean, enum with defaults and validation + - Input/output type specifications (Feature, FeatureCollection, Point, LineString, Polygon, etc.) + - Example definitions and constraint handling + - Implement metadata parsing service in `src/services/metadataParser.ts` + - Handle metadata parsing errors and validation with detailed error messages + - Support schema validation against expected tool definition structure + +2. **Implement dynamic form generation** + - Take note of UI requirements in `docs/ui/readme.md` + - Create `src/components/DynamicForm/` component suite: + - `ParameterForm.tsx` - Main form component + - `ParameterField.tsx` - Individual parameter input fields + - `ParameterValidation.ts` - Validation logic service + - Generate forms dynamically based on Phase 0 parameter schemas supporting: + - Number fields with min/max validation and step controls + - String fields with pattern validation and text areas + - Boolean fields with checkbox/toggle components + - Enum fields with dropdown/radio button selection + - Default value population from Phase 0 tool metadata + - Implement form state management using React hooks with real-time validation + - Add parameter help text display and validation error feedback + - Support parameter dependency handling and conditional field display + +3. **Create input/output rendering system** + - Develop `src/components/DataViewer/` component system: + - `ViewerTabs.tsx` - Tabbed content viewer (Raw, Preview, Download) + - `JSONRenderer.tsx` - Syntax highlighted JSON display + - `GeoJSONRenderer.tsx` - Spatial data preview component + - `FileDownloader.tsx` - File output generation and download + - Support input/output formats from Phase 0 tools: + - GeoJSON (Feature, FeatureCollection) with formatted display + - JSON objects and arrays with collapsible tree view + - String outputs (CSV, REP format) with text formatting + - File outputs with download trigger functionality + - Implement syntax highlighting using `react-syntax-highlighter` + - Handle large input/output rendering with pagination and virtualization + +**Provide Necessary Context/Assets:** +- Phase 0 tools return diverse output types: GeoJSON, JSON objects, strings, arrays +- All parameter definitions include validation rules, defaults, and help text in index.json +- The UI must handle all 12 Phase 0 tool parameter schemas dynamically +- Reference ADR-001 for metadata-driven UI architecture principles +- Output viewer must prepare for LeafletJS integration (Task 2.1) for spatial data + +## 2. Expected Output & Deliverables + +**Define Success:** A complete metadata-driven UI system that dynamically generates forms and output viewers for all Phase 0 tools without hard-coded tool-specific components. + +**Specify Deliverables:** +- `src/services/metadataParser.ts` - Metadata parsing and validation service +- `src/components/DynamicForm/` - Complete dynamic form generation system +- `src/components/DataViewer/` - Multi-format input/output rendering system +- `src/types/metadata.ts` - Comprehensive TypeScript interfaces for tool metadata +- Working form generation for all 12 Phase 0 tool parameter schemas +- Input/output rendering supporting all Phase 0 tool formats +- Form validation and error handling system +- Responsive design supporting different screen sizes + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.3 in the Implementation Plan +- A clear description of the metadata-driven UI implementation +- Key architectural decisions for dynamic form and output rendering +- Any challenges encountered with complex parameter schemas +- Confirmation of successful execution (forms generated correctly for all 12 Phase 0 tools) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md b/prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md new file mode 100644 index 0000000..153acb1 --- /dev/null +++ b/prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md @@ -0,0 +1,98 @@ +# APM Task Assignment: Tool Discovery and Browsing Interface + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.4` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Build intuitive interface for browsing and discovering Phase 0 JavaScript tools with comprehensive filtering and search capabilities. + +**Detailed Action Steps:** + +1. **Implement tool browser component** + - Create `src/components/ToolBrowser/` component system: + - `ToolBrowser.tsx` - Main browser container with grid/list view toggle + - `ToolCard.tsx` - Individual tool metadata display cards + - `CategoryFilter.tsx` - Category-based filtering component + - `ViewControls.tsx` - View mode and sorting controls + - Display Phase 0 tools with metadata cards showing: + - Tool name, description, and category from index.json + - Parameter count and complexity indicators + - Runtime type ("javascript") and version information + - Input/output type specifications + - Organize by Phase 0 tool categories: + - Transform (translate, flip-horizontal, flip-vertical) + - Analysis (speed-series, direction-series) + - Statistics (average-speed, speed-histogram) + - Processing (smooth-polyline) + - I/O (import-rep, export-rep, export-csv) + - Implement responsive design supporting grid (desktop) and list (mobile) views + - Add sorting options: alphabetical, category, complexity + +2. **Add search and filtering capabilities** + - Implement `src/services/toolSearch.ts` search service: + - Text search across Phase 0 tool names and descriptions + - Search indexing for performance with tool metadata + - Search result ranking and relevance scoring + - Search history and recent searches tracking + - Create `src/components/SearchInterface/` components: + - `SearchBar.tsx` - Main search input with autocomplete + - `SearchFilters.tsx` - Advanced filtering controls + - `FilterChips.tsx` - Active filter display and removal + - Support filtering by: + - Category matching Phase 0 tool organization (Transform, Analysis, etc.) + - Runtime type filtering (JavaScript initially, extensible) + - Parameter count ranges (simple vs complex tools) + - Input/output type requirements + - Implement clear/reset filters functionality with state persistence + - Add search suggestions based on Phase 0 tool metadata + +3. **Design tool detail view** + - Create `src/components/ToolDetail/` detailed view components: + - `ToolDetail.tsx` - Complete tool information display + - `ParameterSpecs.tsx` - Parameter specification viewer + - `IOSpecs.tsx` - Input/output type specifications + - `ExampleViewer.tsx` - Tool usage examples from index.json + - Display complete tool information from Phase 0 metadata: + - Full description with usage context + - Detailed parameter specifications with types and defaults + - Input/output requirements and format specifications + - Example usage scenarios from tool metadata + - Version information and commit history + - Add "Try Tool" button to navigate to execution interface (Task 1.5) + - Include breadcrumb navigation and back to browse functionality + - Support deep linking to specific tool detail pages + +**Provide Necessary Context/Assets:** +- All 12 Phase 0 tools have complete metadata in `examples/javascript-bundle/index.json` +- Each tool includes examples array with parameter sets for demonstration +- Tools span 5 categories with varying complexity levels +- Search must be fast and responsive for tool discovery workflow +- Design should accommodate future tool bundles beyond Phase 0 + +## 2. Expected Output & Deliverables + +**Define Success:** An intuitive and efficient tool discovery interface that allows users to easily find and learn about Phase 0 tools before execution. + +**Specify Deliverables:** +- `src/components/ToolBrowser/` - Complete tool browsing interface +- `src/components/SearchInterface/` - Comprehensive search and filtering system +- `src/components/ToolDetail/` - Detailed tool information display +- `src/services/toolSearch.ts` - Search service with indexing and ranking +- Responsive design supporting desktop and mobile browsing +- Working search across all Phase 0 tool metadata +- Category filtering for all 5 Phase 0 tool categories +- Deep linking support for tool detail pages +- State management for filters and search persistence + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.4 in the Implementation Plan +- A clear description of the tool discovery interface implementation +- Key UX decisions for search and browsing workflow +- Any challenges encountered with search performance or filtering +- Confirmation of successful execution (all Phase 0 tools discoverable and browsable) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.5_Tool_Execution_Interface.md b/prompts/tasks/Task_1.5_Tool_Execution_Interface.md new file mode 100644 index 0000000..cee759b --- /dev/null +++ b/prompts/tasks/Task_1.5_Tool_Execution_Interface.md @@ -0,0 +1,88 @@ +# APM Task Assignment: Dynamic Input Forms and Output Renderers + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.5` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Create the execution interface that works with Phase 0 tool specifications, enabling users to run tools with proper input handling and output display. + +**Detailed Action Steps:** + +1. **Build tool execution page** + - Create `src/pages/ToolExecution.tsx` main execution interface: + - Display selected tool information and dynamically generated parameter forms + - Integrate sample data selection from Phase 0 for quick testing + - Implement real-time parameter validation based on Phase 0 schemas + - Add execute button with loading states, progress indication, and cancellation + - Implement `src/components/InputSelection/` components: + - `InputDataSelector.tsx` - Choose between file upload, sample data, or manual input + - `SampleDataPicker.tsx` - Select from Phase 0 sample data (sample-track.geojson, sample-features.geojson) + - `FileUploader.tsx` - Handle GeoJSON, JSON, and text file uploads + - `DataPreview.tsx` - Preview input data before tool execution + - Add input validation matching Phase 0 tool input_types requirements + - Support routing from tool browser with tool ID parameter + +2. **Implement JavaScript tool execution engine** + - Create `src/services/toolExecutor.ts` execution service: + - Execute Phase 0 tools using `window.ToolVault.tools[toolId](input, params)` pattern + - Handle synchronous tool execution with proper error catching and timeout + - Support different input types from Phase 0: Feature, FeatureCollection, Point, LineString, Polygon + - Implement execution logging and performance monitoring + - Add comprehensive error handling: + - Parameter validation errors with specific field feedback + - Tool execution errors with user-friendly messages + - Input format validation and conversion errors + - Timeout handling for long-running operations + - Create execution history tracking using localStorage initially + - Support execution cancellation and cleanup + +3. **Create results display system** + - Develop `src/components/ExecutionResults/` result display system: + - `ResultsContainer.tsx` - Multi-tab output viewer container + - `RawOutput.tsx` - Raw JSON/text output with syntax highlighting + - `FormattedOutput.tsx` - Structured display for specific output types + - `DownloadOutput.tsx` - File download functionality for string outputs + - Support Phase 0 tool output types: + - GeoJSON (Feature, FeatureCollection) with formatted JSON display + - JSON objects and arrays with collapsible tree structures + - String outputs (CSV, REP format) with proper formatting + - Array outputs (time series) with tabular display + - Prepare spatial output tab placeholder for LeafletJS integration (Task 2.1) + - Add execution metadata display: execution time, tool version, parameter summary + - Implement output comparison functionality for multiple executions + - Support output export and sharing capabilities + +**Provide Necessary Context/Assets:** +- Phase 0 sample data files: `examples/javascript-bundle/data/sample-track.geojson`, `sample-features.geojson` +- All Phase 0 tools expect specific input formats defined in their input_types metadata +- Tools return diverse outputs: GeoJSON objects, JSON arrays, formatted strings +- Phase 0 tools are synchronous and execute in browser context via window.ToolVault.tools +- Execution interface must handle all 12 Phase 0 tools with their unique parameter requirements + +## 2. Expected Output & Deliverables + +**Define Success:** A complete tool execution interface that allows users to run any Phase 0 tool with appropriate inputs and view results in multiple formats. + +**Specify Deliverables:** +- `src/pages/ToolExecution.tsx` - Main tool execution page +- `src/components/InputSelection/` - Complete input handling system +- `src/components/ExecutionResults/` - Multi-format results display +- `src/services/toolExecutor.ts` - Tool execution service with error handling +- Working execution for all 12 Phase 0 tools with sample data +- Parameter validation and error feedback system +- Execution history tracking and result comparison +- File upload and download functionality +- Loading states and progress indication + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.5 in the Implementation Plan +- A clear description of the tool execution interface implementation +- Key decisions for input handling and output rendering architecture +- Any challenges encountered with Phase 0 tool execution or error handling +- Confirmation of successful execution (all 12 Phase 0 tools executable with proper I/O handling) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md b/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md new file mode 100644 index 0000000..fcb4ce8 --- /dev/null +++ b/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md @@ -0,0 +1,124 @@ +# APM Task Assignment: GitHub Pages Deployment + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.6` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Deploy the working application with Phase 0 tools for stakeholder review, creating a production-ready instance for testing and validation. + +**Detailed Action Steps:** + +1. **Configure deployment pipeline** + - Create `.github/workflows/deploy.yml` GitHub Actions workflow: + - Automated build triggers on main branch push and pull requests + - Node.js setup with proper version specification (18.x or 20.x) + - Install dependencies and run build process + - Include Phase 0 bundle assets in build artifact + - Configure Vite build for GitHub Pages deployment: + - Set proper base path in `vite.config.ts` for GitHub Pages subdirectory + - Ensure all Phase 0 JavaScript bundle files are included in build output + - Configure asset handling for `examples/javascript-bundle/` integration + - Optimize bundle size with code splitting and tree shaking + - Handle client-side routing compatibility: + - Create `404.html` fallback for React Router navigation + - Configure proper redirect handling for SPA routing + - Test routing works correctly in GitHub Pages environment + +2. **Deploy to GitHub Pages** + - Configure repository settings for GitHub Pages: + - Enable GitHub Pages from GitHub Actions deployment source + - Set custom domain if available or use default github.io subdomain + - Configure HTTPS and security settings + - Ensure Phase 0 JavaScript bundle accessibility: + - Verify all tool scripts load correctly in production environment + - Test dynamic script loading from `examples/javascript-bundle/tools/` + - Confirm `index.json` metadata loading and parsing + - Validate CORS settings for cross-origin resource loading + - Create production deployment verification: + - Test all 12 Phase 0 tools execute correctly in production + - Verify tool parameter handling and output rendering + - Test file upload/download functionality + - Confirm responsive design works across devices + +3. **Create deployment documentation and testing guides** + - Create `docs/deployment.md` documentation: + - Step-by-step deployment process including Phase 0 bundle integration + - Troubleshooting guide for common deployment issues + - Environment configuration and build optimization notes + - Instructions for updating deployed version with new Phase 0 tools + - Create `docs/stakeholder-testing-guide.md`: + - User guide for stakeholder testing of Phase 0 tool functionality + - Test scenarios covering all 5 tool categories (Transform, Analysis, Statistics, Processing, I/O) + - Expected outputs and validation criteria for each tool type + - Known limitations and future enhancement roadmap + - Set up feedback collection mechanism: + - Create GitHub issue templates for bug reports and feature requests + - Add feedback link in application header/footer + - Configure issue labels for different types of feedback (UI, tools, performance) + - Prepare stakeholder demo script: + - Highlight Phase 0 tool capabilities with specific examples + - Demonstrate tool discovery, execution, and output rendering workflow + - Show metadata-driven UI generation and dynamic form creation + - Present tool execution with sample data and real-world scenarios + +**Provide Necessary Context/Assets:** +- The deployment must include all Phase 0 JavaScript tools from `examples/javascript-bundle/` +- All 12 Phase 0 tools must be functional in the production environment +- Sample data files must be accessible for testing tool execution +- The application uses client-side routing which requires special GitHub Pages configuration +- Deployment should serve as validation environment for stakeholder feedback + +## 2. Expected Output & Deliverables + +**Define Success:** A fully functional ToolVault application deployed on GitHub Pages with all Phase 0 tools working correctly and comprehensive testing documentation. + +**Specify Deliverables:** +- `.github/workflows/deploy.yml` - Automated deployment pipeline +- `vite.config.ts` - Production build configuration for GitHub Pages +- `404.html` - SPA routing fallback page +- `docs/deployment.md` - Comprehensive deployment documentation +- `docs/stakeholder-testing-guide.md` - User testing guide +- GitHub issue templates for feedback collection +- Working production deployment with all 12 Phase 0 tools functional +- Stakeholder demo script highlighting key capabilities +- Performance optimization and bundle size analysis + +## 3. E2E Testing Requirements + +Create comprehensive end-to-end tests to ensure the deployed service runs correctly: + +1. **Service Availability Tests** + - Verify application loads correctly at GitHub Pages URL + - Test all routes and navigation functionality + - Confirm asset loading (CSS, JS, images) without errors + +2. **Phase 0 Tool Integration Tests** + - Test tool discovery and browsing for all 12 Phase 0 tools + - Verify dynamic form generation for each tool's parameter schema + - Execute each tool with sample data and validate outputs: + - Transform tools: translate, flip-horizontal, flip-vertical + - Analysis tools: speed-series, direction-series + - Statistics tools: average-speed, speed-histogram + - Processing tools: smooth-polyline + - I/O tools: import-rep, export-rep, export-csv + - Test error handling and validation across all tools + +3. **User Workflow Tests** + - Complete user journey from tool discovery to execution + - File upload and sample data selection functionality + - Output viewing and download capabilities + - Search and filtering across tool catalog + - Responsive design validation on mobile and desktop + +## 4. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.6 in the Implementation Plan +- A clear description of the deployment pipeline and configuration +- Production URL and deployment verification results +- Any challenges encountered with GitHub Pages or Phase 0 tool integration +- Confirmation of successful execution (all Phase 0 tools working in production with E2E test results) + +## 5. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file From ccbf60f584d8a6fe41770230f11723c1aa02f054 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Wed, 20 Aug 2025 09:11:48 +0100 Subject: [PATCH 14/18] drop execution history references --- CLAUDE.md | 2 +- Implementation_Plan.md | 42 +++++++++---------- README.md | 4 +- .../ADR-009-storage-and-caching-strategy.md | 1 - docs/business_analysis.md | 2 +- docs/software_requirements.md | 4 +- docs/ui/readme.md | 3 +- 7 files changed, 28 insertions(+), 30 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8c4e463..9e88e74 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -ToolVault is a portable, self-contained service that delivers curated collections of analysis tools for scientists, analysts, and developers. It provides an interactive browser-based interface for discovering, running, and understanding tools with history tracking, version awareness, and spatial output visualization. +ToolVault is a portable, self-contained service that delivers curated collections of analysis tools for scientists, analysts, and developers. It provides an interactive browser-based interface for discovering, running, and understanding tools version awareness, and spatial output visualization. ## Project Structure diff --git a/Implementation_Plan.md b/Implementation_Plan.md index 3c86984..837debf 100644 --- a/Implementation_Plan.md +++ b/Implementation_Plan.md @@ -179,44 +179,44 @@ Objective: Integrate the JavaScript toolbox created in Phase 0 as the standard t - Handle script loading errors gracefully - Test integration with all 10-15 tools from Phase 0 -### Task 1.3 - Agent_Frontend_Lead: Metadata-Driven UI from index.json -Objective: Create dynamic UI components that generate interfaces directly from Phase 0 tool metadata. +### Task 1.3 - Agent_Frontend_Lead: Tool Discovery and Browsing Interface +Objective: Build intuitive interface for browsing and discovering Phase 0 JavaScript tools. 1. Parse tool metadata from Phase 0 index.json. - Load and validate index.json structure with runtime field - Create TypeScript interfaces for tool definitions and parameters - Support parameter constraints and validation rules - Handle metadata parsing errors and validation -2. Implement dynamic form generation. - - Generate forms dynamically based on Phase 0 parameter schemas - - Support all parameter types used in Phase 0 tools (number, string, boolean, enum) - - Implement form state management with validation - - Add parameter help text and validation error display -3. Create output rendering system. - - Support output formats from Phase 0: GeoJSON, JSON, Files - - Implement tabbed output viewer (Raw, Preview, Download) - - Add syntax highlighting for JSON/GeoJSON outputs - - Handle file output generation and download triggers - -### Task 1.4 - Agent_Frontend_Lead: Tool Discovery and Browsing Interface -Objective: Build intuitive interface for browsing and discovering Phase 0 JavaScript tools. - -1. Implement tool browser component. +2. Implement tool browser component. - Create grid/list view showing Phase 0 tools with metadata cards - Display tool categories: Transform, Analysis, Statistics, Processing, I/O - Show tool name, description, parameter count, and runtime type - Implement responsive design for different screen sizes -2. Add search and filtering capabilities. +3. Add search and filtering capabilities. - Implement text search across Phase 0 tool names and descriptions - Add category-based filtering matching Phase 0 tool organization - Support runtime type filtering (JavaScript initially) - Create clear/reset filters functionality -3. Design tool detail view. +4. Design tool detail view. - Show complete tool information from Phase 0 metadata - Display input/output specifications and parameter details - Add "Try Tool" button to navigate to execution interface - Include tool metadata like version and runtime requirements +### Task 1.4 - Agent_Frontend_Lead: Metadata-Driven UI from index.json +Objective: Create dynamic UI components that generate interfaces directly from Phase 0 tool metadata. + +1. Implement dynamic form generation. + - Generate forms dynamically based on Phase 0 parameter schemas + - Support all parameter types used in Phase 0 tools (number, string, boolean, enum) + - Implement form state management with validation + - Add parameter help text and validation error display +2. Create input/output rendering system. + - Support input/output formats from Phase 0: GeoJSON, JSON, Files + - Implement tabbed input/output viewer (Raw, Preview, Download) + - Add syntax highlighting for JSON/GeoJSON input/outputs + - Handle file input/output generation and download triggers + ### Task 1.5 - Agent_Frontend_Lead: Dynamic Input Forms and Output Renderers Objective: Create the execution interface that works with Phase 0 tool specifications. @@ -231,8 +231,8 @@ Objective: Create the execution interface that works with Phase 0 tool specifica - Support different input types including Phase 0 sample data - Implement execution timeout and error recovery 3. Create results display system. - - Multi-tab output viewer for Phase 0 tool outputs - - Support GeoJSON, JSON, and file download outputs from Phase 0 + - Multi-tab input/output viewer for Phase 0 tool inputs/outputs + - Support GeoJSON, JSON, and file download inputs/outputs from Phase 0 - Prepare for LeafletJS integration for GeoJSON visualization - Add execution history tracking (local storage initially) diff --git a/README.md b/README.md index 0880aa8..0f8d412 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ToolVault is a portable, self-contained service that delivers curated collections of analysis tools for scientists, analysts, and developers. It provides an interactive browser-based interface for -discovering, running, and understanding tools — complete with history, legacy version access, and +discovering, running, and understanding tools — complete with git history, legacy version access, and spatial output visualisation. ## Key Features @@ -24,7 +24,7 @@ We are in the **early UI mockup phase**, focusing on: 1. **Phase 1 – UI Mockup** - Bare static UI from `index.json` (browse tools, view details, inputs/outputs, run button). - Mock tool calls with placeholder outputs. - - Full browsing UI with LeafletJS output rendering and history view placeholder. + - Full browsing UI with LeafletJS output rendering and git history view placeholder. 2. **Phase 2 – Indexer Development** - ToolVault indexer to scan a Git repo of tools, generate `index.json` and metadata. diff --git a/docs/ADRs/ADR-009-storage-and-caching-strategy.md b/docs/ADRs/ADR-009-storage-and-caching-strategy.md index 79ae9e0..f89bcc8 100644 --- a/docs/ADRs/ADR-009-storage-and-caching-strategy.md +++ b/docs/ADRs/ADR-009-storage-and-caching-strategy.md @@ -25,7 +25,6 @@ ToolVault requires a storage and caching strategy that supports: ### Core Components - **Tool Bundles**: File system storage managed by FastAPI backend -- **Execution History**: Session-only, no persistence across restarts - **Browser Storage**: Completely avoided - no LocalStorage/IndexedDB usage - **Updates**: Multi-modal approach (manual + startup check + background) diff --git a/docs/business_analysis.md b/docs/business_analysis.md index 33cd6dc..830c9c7 100644 --- a/docs/business_analysis.md +++ b/docs/business_analysis.md @@ -115,7 +115,7 @@ ToolVault addresses the organization's need for reproducible, accessible, and st **Tool Discovery** - Find appropriate tools through search and browsing - Interactive testing before application -- Version history shows tool evolution +- Version history shows tool evolution from git log - Related tools and common patterns visible **Pipeline Templates** diff --git a/docs/software_requirements.md b/docs/software_requirements.md index 7df844f..7a7c993 100644 --- a/docs/software_requirements.md +++ b/docs/software_requirements.md @@ -33,7 +33,7 @@ ToolVault SHALL provide a metadata-driven interface for tool discovery, executio **FR-1.4** The system SHALL provide an interactive tool detail page allowing users to test tools with sample data. -**FR-1.5** The system SHALL display version history for individual tools and tool collections. +**FR-1.5** The system SHALL display git version history for individual tools and tool collections. ### 2.2 Input Management @@ -328,7 +328,7 @@ ToolVault SHALL provide a metadata-driven interface for tool discovery, executio **Tech-1.4** The system SHALL organize code using domain-driven structure: - `/tools/` - Tool discovery, metadata, and browsing - `/execution/` - Tool execution and parameter management -- `/history/` - Execution history and provenance tracking +- `/history/` - Source history and provenance tracking (from git) - `/visualization/` - Spatial output rendering with LeafletJS - `/shared/` - Common components, types, and utilities diff --git a/docs/ui/readme.md b/docs/ui/readme.md index f50e3a4..160b94c 100644 --- a/docs/ui/readme.md +++ b/docs/ui/readme.md @@ -40,7 +40,7 @@ Based on user journey: Homepage → Browse Tools → Tool Detail → Execution - **ToolTabs**: Tab interface (Overview, Example, History) - **ToolOverview**: Description, metadata, inputs/outputs - **ToolExample**: Input/Output components, plus trigger button. -- **ToolHistory**: Execution history display (placeholder initially) +- **ToolHistory**: Git source history display (placeholder initially) - **InputOutputVisualization**: Shows data types and formats - **ExecutionInterface**: Run button positioned between input/output visualizations @@ -57,7 +57,6 @@ Based on user journey: Homepage → Browse Tools → Tool Detail → Execution - **RunButton**: Central execution trigger - **ExecutionStatus**: Progress indicator and status display - **ResultDisplay**: Container for tool execution results -- **HistoryEntry**: Individual execution record ### 7. Utility Components - **LoadingSpinner**: Loading states From 7ed788db56493ea1929c520bd279d9090257ebd1 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Wed, 20 Aug 2025 09:12:23 +0100 Subject: [PATCH 15/18] drop TAPs, prior to regeneration --- .../Task_1.1_React_TypeScript_SPA_Setup.md | 71 ---------- ..._1.2_Load_JavaScript_Bundle_Integration.md | 74 ----------- .../Task_1.3_Metadata_Driven_UI_Generation.md | 84 ------------ .../Task_1.4_Tool_Discovery_and_Browsing.md | 98 -------------- .../Task_1.5_Tool_Execution_Interface.md | 88 ------------- .../tasks/Task_1.6_GitHub_Pages_Deployment.md | 124 ------------------ 6 files changed, 539 deletions(-) delete mode 100644 prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md delete mode 100644 prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md delete mode 100644 prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md delete mode 100644 prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md delete mode 100644 prompts/tasks/Task_1.5_Tool_Execution_Interface.md delete mode 100644 prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md diff --git a/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md b/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md deleted file mode 100644 index 6e06bcb..0000000 --- a/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md +++ /dev/null @@ -1,71 +0,0 @@ -# APM Task Assignment: React/TypeScript SPA Setup - -## 1. Agent Role & APM Context - -You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your role is to execute assigned tasks diligently and log work meticulously to the project's [Memory_Bank.md](../../Memory_Bank.md) file. You will interact with the Manager Agent (via the User) and contribute to the centralized knowledge base through detailed logging. - -## 2. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.1` in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** Establish the core frontend application infrastructure using modern web development tools to support the JavaScript tool bundle created in Phase 0. - -**Detailed Action Steps:** - -1. **Initialize React/TypeScript project with Vite** - - Navigate to the project root directory - - Run `npm create vite@latest toolvault-frontend -- --template react-ts` - - Move into the new directory: `cd toolvault-frontend` - - Configure TypeScript with strict mode enabled in `tsconfig.json` - - Set up project directory structure: - - `/src/components` - React components - - `/src/services` - API and tool loading services - - `/src/types` - TypeScript interface definitions - - `/src/utils` - Utility functions - - Configure Vite for development and production builds with proper base path settings - -2. **Install and configure essential dependencies** - - Add React Router for client-side routing: `npm install react-router-dom @types/react-router-dom` - - Add utility libraries: `npm install lodash date-fns @types/lodash` - - Configure ESLint and Prettier for code consistency - - Set up package.json scripts for development workflow - -3. **Set up development environment** - - Create `.vscode/settings.json` and `.vscode/extensions.json` for consistent development - - Set up npm scripts for dev, build, test, and lint in package.json - - Create basic folder structure and initial components: - - `src/App.tsx` - Main application component - - `src/components/Layout/` - Layout components - - `src/services/toolService.ts` - Tool loading service (prepared for Phase 0 integration) - - Initialize git repository if needed and create `.gitignore` - -**Provide Necessary Context/Assets:** -- The application must be designed to integrate with the JavaScript tool bundle from `examples/javascript-bundle/` -- Reference `examples/javascript-bundle/index.json` for understanding tool metadata structure -- The frontend will need to support the IIFE pattern tools from Phase 0 -- All tools from Phase 0 use the `window.ToolVault.tools` namespace - -## 3. Expected Output & Deliverables - -**Define Success:** A fully functional React/TypeScript application with Vite build system that can serve as the foundation for integrating Phase 0 JavaScript tools. - -**Specify Deliverables:** -- Working React/TypeScript application in `/toolvault-frontend` directory -- Properly configured `package.json` with all required dependencies -- TypeScript configuration with strict mode enabled -- ESLint and Prettier configuration files -- Basic component structure and routing setup -- Development scripts that run without errors: `npm run dev`, `npm run build`, `npm run lint` - -## 4. Memory Bank Logging Instructions - -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to Phase 1, Task 1.1 in the Implementation Plan -- A clear description of the React/TypeScript setup actions taken -- Key configuration decisions made (TypeScript settings, dependencies chosen) -- Any challenges encountered during setup -- Confirmation of successful execution (dev server running, build succeeds) - -## 5. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md b/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md deleted file mode 100644 index 07683e1..0000000 --- a/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md +++ /dev/null @@ -1,74 +0,0 @@ -# APM Task Assignment: Load Standard Bundle from Phase 0 - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.2` in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** Integrate the JavaScript toolbox created in Phase 0 as the standard tool bundle for the React frontend. - -**Detailed Action Steps:** - -1. **Create bundle loading service** - - Reference `/examples/javascript-bundle/index.json` as the primary bundle source - - Implement `src/services/bundleLoader.ts` to fetch and parse tool metadata from Phase 0 - - Create TypeScript interfaces in `src/types/tools.ts` matching Phase 0 tool definitions: - - Interface for tool metadata structure from `index.json` - - Parameter schema interfaces (number, string, boolean, enum types) - - Input/output type definitions (Feature, FeatureCollection, etc.) - - Handle loading errors with fallback mechanisms and user-friendly error messages - - Support both development (local file) and production (bundled) loading scenarios - -2. **Implement tool registry system** - - Create `src/services/toolRegistry.ts` for in-memory registry of Phase 0 bundle tools - - Implement filtering capabilities by: - - Category (Transform, Analysis, Statistics, Processing, I/O - note: these should not be hard-coded, but derivte from the metadata) - - Runtime type ("javascript") - - Search terms across tool names and descriptions - - Implement tool metadata caching for performance optimization - - Add tool availability status tracking and health checks - - Create methods for: `getTools()`, `getToolById()`, `getToolsByCategory()`, `searchTools()` - -3. **Load Phase 0 JavaScript tools** - - Implement dynamic script loading in `src/services/scriptLoader.ts` using IIFE pattern from Phase 0 - - Verify tool function availability in `window.ToolVault.tools` namespace after loading - - Handle script loading errors gracefully with retry mechanisms - - Test integration with all 12 tools from Phase 0: - - Transform: translate, flip-horizontal, flip-vertical - - Analysis: speed-series, direction-series - - Statistics: average-speed, speed-histogram - - Processing: smooth-polyline - - I/O: import-rep, export-rep, export-csv - - Create tool execution wrapper with error handling and validation - -**Provide Necessary Context/Assets:** -- Phase 0 tools are implemented using IIFE pattern and register in `window.ToolVault.tools` namespace -- The `examples/javascript-bundle/index.json` contains complete metadata with runtime field = "javascript" -- All Phase 0 tools expect specific input formats: GeoJSON Feature/FeatureCollection, LineString, etc. -- Phase 0 tools include comprehensive parameter schemas with defaults and validation rules -- Reference ADR-013 for JavaScript tool implementation specification - -## 2. Expected Output & Deliverables - -**Define Success:** A fully functional bundle loading system that can dynamically load and execute all Phase 0 JavaScript tools from the frontend. - -**Specify Deliverables:** -- `src/services/bundleLoader.ts` - Bundle metadata loading service -- `src/services/toolRegistry.ts` - Tool registry and filtering system -- `src/services/scriptLoader.ts` - Dynamic script loading for IIFE tools -- `src/types/tools.ts` - TypeScript interfaces for Phase 0 tool structure -- Successful loading and execution test of all 12 Phase 0 tools -- Error handling for bundle loading failures -- Tool availability verification system - -## 3. Memory Bank Logging Instructions - -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to Phase 1, Task 1.2 in the Implementation Plan -- A clear description of the bundle integration implementation -- Key decisions made for tool loading architecture -- Any challenges encountered with Phase 0 tool integration -- Confirmation of successful execution (all 12 Phase 0 tools loading and executing correctly) - -## 4. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md b/prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md deleted file mode 100644 index c2299d8..0000000 --- a/prompts/tasks/Task_1.3_Metadata_Driven_UI_Generation.md +++ /dev/null @@ -1,84 +0,0 @@ -# APM Task Assignment: Metadata-Driven UI from index.json - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.3` in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** Create dynamic UI components that generate interfaces directly from Phase 0 tool metadata, following the metadata-driven UI architecture (ADR-001). - -**Detailed Action Steps:** - -1. **Parse tool metadata from Phase 0 index.json** - - Load and validate `examples/javascript-bundle/index.json` structure with runtime field - - Create comprehensive TypeScript interfaces in `src/types/` for: - - Tool definitions with parameter schemas - - Parameter types: number, string, boolean, enum with defaults and validation - - Input/output type specifications (Feature, FeatureCollection, Point, LineString, Polygon, etc.) - - Example definitions and constraint handling - - Implement metadata parsing service in `src/services/metadataParser.ts` - - Handle metadata parsing errors and validation with detailed error messages - - Support schema validation against expected tool definition structure - -2. **Implement dynamic form generation** - - Take note of UI requirements in `docs/ui/readme.md` - - Create `src/components/DynamicForm/` component suite: - - `ParameterForm.tsx` - Main form component - - `ParameterField.tsx` - Individual parameter input fields - - `ParameterValidation.ts` - Validation logic service - - Generate forms dynamically based on Phase 0 parameter schemas supporting: - - Number fields with min/max validation and step controls - - String fields with pattern validation and text areas - - Boolean fields with checkbox/toggle components - - Enum fields with dropdown/radio button selection - - Default value population from Phase 0 tool metadata - - Implement form state management using React hooks with real-time validation - - Add parameter help text display and validation error feedback - - Support parameter dependency handling and conditional field display - -3. **Create input/output rendering system** - - Develop `src/components/DataViewer/` component system: - - `ViewerTabs.tsx` - Tabbed content viewer (Raw, Preview, Download) - - `JSONRenderer.tsx` - Syntax highlighted JSON display - - `GeoJSONRenderer.tsx` - Spatial data preview component - - `FileDownloader.tsx` - File output generation and download - - Support input/output formats from Phase 0 tools: - - GeoJSON (Feature, FeatureCollection) with formatted display - - JSON objects and arrays with collapsible tree view - - String outputs (CSV, REP format) with text formatting - - File outputs with download trigger functionality - - Implement syntax highlighting using `react-syntax-highlighter` - - Handle large input/output rendering with pagination and virtualization - -**Provide Necessary Context/Assets:** -- Phase 0 tools return diverse output types: GeoJSON, JSON objects, strings, arrays -- All parameter definitions include validation rules, defaults, and help text in index.json -- The UI must handle all 12 Phase 0 tool parameter schemas dynamically -- Reference ADR-001 for metadata-driven UI architecture principles -- Output viewer must prepare for LeafletJS integration (Task 2.1) for spatial data - -## 2. Expected Output & Deliverables - -**Define Success:** A complete metadata-driven UI system that dynamically generates forms and output viewers for all Phase 0 tools without hard-coded tool-specific components. - -**Specify Deliverables:** -- `src/services/metadataParser.ts` - Metadata parsing and validation service -- `src/components/DynamicForm/` - Complete dynamic form generation system -- `src/components/DataViewer/` - Multi-format input/output rendering system -- `src/types/metadata.ts` - Comprehensive TypeScript interfaces for tool metadata -- Working form generation for all 12 Phase 0 tool parameter schemas -- Input/output rendering supporting all Phase 0 tool formats -- Form validation and error handling system -- Responsive design supporting different screen sizes - -## 3. Memory Bank Logging Instructions - -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to Phase 1, Task 1.3 in the Implementation Plan -- A clear description of the metadata-driven UI implementation -- Key architectural decisions for dynamic form and output rendering -- Any challenges encountered with complex parameter schemas -- Confirmation of successful execution (forms generated correctly for all 12 Phase 0 tools) - -## 4. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md b/prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md deleted file mode 100644 index 153acb1..0000000 --- a/prompts/tasks/Task_1.4_Tool_Discovery_and_Browsing.md +++ /dev/null @@ -1,98 +0,0 @@ -# APM Task Assignment: Tool Discovery and Browsing Interface - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.4` in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** Build intuitive interface for browsing and discovering Phase 0 JavaScript tools with comprehensive filtering and search capabilities. - -**Detailed Action Steps:** - -1. **Implement tool browser component** - - Create `src/components/ToolBrowser/` component system: - - `ToolBrowser.tsx` - Main browser container with grid/list view toggle - - `ToolCard.tsx` - Individual tool metadata display cards - - `CategoryFilter.tsx` - Category-based filtering component - - `ViewControls.tsx` - View mode and sorting controls - - Display Phase 0 tools with metadata cards showing: - - Tool name, description, and category from index.json - - Parameter count and complexity indicators - - Runtime type ("javascript") and version information - - Input/output type specifications - - Organize by Phase 0 tool categories: - - Transform (translate, flip-horizontal, flip-vertical) - - Analysis (speed-series, direction-series) - - Statistics (average-speed, speed-histogram) - - Processing (smooth-polyline) - - I/O (import-rep, export-rep, export-csv) - - Implement responsive design supporting grid (desktop) and list (mobile) views - - Add sorting options: alphabetical, category, complexity - -2. **Add search and filtering capabilities** - - Implement `src/services/toolSearch.ts` search service: - - Text search across Phase 0 tool names and descriptions - - Search indexing for performance with tool metadata - - Search result ranking and relevance scoring - - Search history and recent searches tracking - - Create `src/components/SearchInterface/` components: - - `SearchBar.tsx` - Main search input with autocomplete - - `SearchFilters.tsx` - Advanced filtering controls - - `FilterChips.tsx` - Active filter display and removal - - Support filtering by: - - Category matching Phase 0 tool organization (Transform, Analysis, etc.) - - Runtime type filtering (JavaScript initially, extensible) - - Parameter count ranges (simple vs complex tools) - - Input/output type requirements - - Implement clear/reset filters functionality with state persistence - - Add search suggestions based on Phase 0 tool metadata - -3. **Design tool detail view** - - Create `src/components/ToolDetail/` detailed view components: - - `ToolDetail.tsx` - Complete tool information display - - `ParameterSpecs.tsx` - Parameter specification viewer - - `IOSpecs.tsx` - Input/output type specifications - - `ExampleViewer.tsx` - Tool usage examples from index.json - - Display complete tool information from Phase 0 metadata: - - Full description with usage context - - Detailed parameter specifications with types and defaults - - Input/output requirements and format specifications - - Example usage scenarios from tool metadata - - Version information and commit history - - Add "Try Tool" button to navigate to execution interface (Task 1.5) - - Include breadcrumb navigation and back to browse functionality - - Support deep linking to specific tool detail pages - -**Provide Necessary Context/Assets:** -- All 12 Phase 0 tools have complete metadata in `examples/javascript-bundle/index.json` -- Each tool includes examples array with parameter sets for demonstration -- Tools span 5 categories with varying complexity levels -- Search must be fast and responsive for tool discovery workflow -- Design should accommodate future tool bundles beyond Phase 0 - -## 2. Expected Output & Deliverables - -**Define Success:** An intuitive and efficient tool discovery interface that allows users to easily find and learn about Phase 0 tools before execution. - -**Specify Deliverables:** -- `src/components/ToolBrowser/` - Complete tool browsing interface -- `src/components/SearchInterface/` - Comprehensive search and filtering system -- `src/components/ToolDetail/` - Detailed tool information display -- `src/services/toolSearch.ts` - Search service with indexing and ranking -- Responsive design supporting desktop and mobile browsing -- Working search across all Phase 0 tool metadata -- Category filtering for all 5 Phase 0 tool categories -- Deep linking support for tool detail pages -- State management for filters and search persistence - -## 3. Memory Bank Logging Instructions - -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to Phase 1, Task 1.4 in the Implementation Plan -- A clear description of the tool discovery interface implementation -- Key UX decisions for search and browsing workflow -- Any challenges encountered with search performance or filtering -- Confirmation of successful execution (all Phase 0 tools discoverable and browsable) - -## 4. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.5_Tool_Execution_Interface.md b/prompts/tasks/Task_1.5_Tool_Execution_Interface.md deleted file mode 100644 index cee759b..0000000 --- a/prompts/tasks/Task_1.5_Tool_Execution_Interface.md +++ /dev/null @@ -1,88 +0,0 @@ -# APM Task Assignment: Dynamic Input Forms and Output Renderers - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.5` in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** Create the execution interface that works with Phase 0 tool specifications, enabling users to run tools with proper input handling and output display. - -**Detailed Action Steps:** - -1. **Build tool execution page** - - Create `src/pages/ToolExecution.tsx` main execution interface: - - Display selected tool information and dynamically generated parameter forms - - Integrate sample data selection from Phase 0 for quick testing - - Implement real-time parameter validation based on Phase 0 schemas - - Add execute button with loading states, progress indication, and cancellation - - Implement `src/components/InputSelection/` components: - - `InputDataSelector.tsx` - Choose between file upload, sample data, or manual input - - `SampleDataPicker.tsx` - Select from Phase 0 sample data (sample-track.geojson, sample-features.geojson) - - `FileUploader.tsx` - Handle GeoJSON, JSON, and text file uploads - - `DataPreview.tsx` - Preview input data before tool execution - - Add input validation matching Phase 0 tool input_types requirements - - Support routing from tool browser with tool ID parameter - -2. **Implement JavaScript tool execution engine** - - Create `src/services/toolExecutor.ts` execution service: - - Execute Phase 0 tools using `window.ToolVault.tools[toolId](input, params)` pattern - - Handle synchronous tool execution with proper error catching and timeout - - Support different input types from Phase 0: Feature, FeatureCollection, Point, LineString, Polygon - - Implement execution logging and performance monitoring - - Add comprehensive error handling: - - Parameter validation errors with specific field feedback - - Tool execution errors with user-friendly messages - - Input format validation and conversion errors - - Timeout handling for long-running operations - - Create execution history tracking using localStorage initially - - Support execution cancellation and cleanup - -3. **Create results display system** - - Develop `src/components/ExecutionResults/` result display system: - - `ResultsContainer.tsx` - Multi-tab output viewer container - - `RawOutput.tsx` - Raw JSON/text output with syntax highlighting - - `FormattedOutput.tsx` - Structured display for specific output types - - `DownloadOutput.tsx` - File download functionality for string outputs - - Support Phase 0 tool output types: - - GeoJSON (Feature, FeatureCollection) with formatted JSON display - - JSON objects and arrays with collapsible tree structures - - String outputs (CSV, REP format) with proper formatting - - Array outputs (time series) with tabular display - - Prepare spatial output tab placeholder for LeafletJS integration (Task 2.1) - - Add execution metadata display: execution time, tool version, parameter summary - - Implement output comparison functionality for multiple executions - - Support output export and sharing capabilities - -**Provide Necessary Context/Assets:** -- Phase 0 sample data files: `examples/javascript-bundle/data/sample-track.geojson`, `sample-features.geojson` -- All Phase 0 tools expect specific input formats defined in their input_types metadata -- Tools return diverse outputs: GeoJSON objects, JSON arrays, formatted strings -- Phase 0 tools are synchronous and execute in browser context via window.ToolVault.tools -- Execution interface must handle all 12 Phase 0 tools with their unique parameter requirements - -## 2. Expected Output & Deliverables - -**Define Success:** A complete tool execution interface that allows users to run any Phase 0 tool with appropriate inputs and view results in multiple formats. - -**Specify Deliverables:** -- `src/pages/ToolExecution.tsx` - Main tool execution page -- `src/components/InputSelection/` - Complete input handling system -- `src/components/ExecutionResults/` - Multi-format results display -- `src/services/toolExecutor.ts` - Tool execution service with error handling -- Working execution for all 12 Phase 0 tools with sample data -- Parameter validation and error feedback system -- Execution history tracking and result comparison -- File upload and download functionality -- Loading states and progress indication - -## 3. Memory Bank Logging Instructions - -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to Phase 1, Task 1.5 in the Implementation Plan -- A clear description of the tool execution interface implementation -- Key decisions for input handling and output rendering architecture -- Any challenges encountered with Phase 0 tool execution or error handling -- Confirmation of successful execution (all 12 Phase 0 tools executable with proper I/O handling) - -## 4. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md b/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md deleted file mode 100644 index fcb4ce8..0000000 --- a/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md +++ /dev/null @@ -1,124 +0,0 @@ -# APM Task Assignment: GitHub Pages Deployment - -## 1. Task Assignment - -**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.6` in the [Implementation_Plan.md](../../Implementation_Plan.md). - -**Objective:** Deploy the working application with Phase 0 tools for stakeholder review, creating a production-ready instance for testing and validation. - -**Detailed Action Steps:** - -1. **Configure deployment pipeline** - - Create `.github/workflows/deploy.yml` GitHub Actions workflow: - - Automated build triggers on main branch push and pull requests - - Node.js setup with proper version specification (18.x or 20.x) - - Install dependencies and run build process - - Include Phase 0 bundle assets in build artifact - - Configure Vite build for GitHub Pages deployment: - - Set proper base path in `vite.config.ts` for GitHub Pages subdirectory - - Ensure all Phase 0 JavaScript bundle files are included in build output - - Configure asset handling for `examples/javascript-bundle/` integration - - Optimize bundle size with code splitting and tree shaking - - Handle client-side routing compatibility: - - Create `404.html` fallback for React Router navigation - - Configure proper redirect handling for SPA routing - - Test routing works correctly in GitHub Pages environment - -2. **Deploy to GitHub Pages** - - Configure repository settings for GitHub Pages: - - Enable GitHub Pages from GitHub Actions deployment source - - Set custom domain if available or use default github.io subdomain - - Configure HTTPS and security settings - - Ensure Phase 0 JavaScript bundle accessibility: - - Verify all tool scripts load correctly in production environment - - Test dynamic script loading from `examples/javascript-bundle/tools/` - - Confirm `index.json` metadata loading and parsing - - Validate CORS settings for cross-origin resource loading - - Create production deployment verification: - - Test all 12 Phase 0 tools execute correctly in production - - Verify tool parameter handling and output rendering - - Test file upload/download functionality - - Confirm responsive design works across devices - -3. **Create deployment documentation and testing guides** - - Create `docs/deployment.md` documentation: - - Step-by-step deployment process including Phase 0 bundle integration - - Troubleshooting guide for common deployment issues - - Environment configuration and build optimization notes - - Instructions for updating deployed version with new Phase 0 tools - - Create `docs/stakeholder-testing-guide.md`: - - User guide for stakeholder testing of Phase 0 tool functionality - - Test scenarios covering all 5 tool categories (Transform, Analysis, Statistics, Processing, I/O) - - Expected outputs and validation criteria for each tool type - - Known limitations and future enhancement roadmap - - Set up feedback collection mechanism: - - Create GitHub issue templates for bug reports and feature requests - - Add feedback link in application header/footer - - Configure issue labels for different types of feedback (UI, tools, performance) - - Prepare stakeholder demo script: - - Highlight Phase 0 tool capabilities with specific examples - - Demonstrate tool discovery, execution, and output rendering workflow - - Show metadata-driven UI generation and dynamic form creation - - Present tool execution with sample data and real-world scenarios - -**Provide Necessary Context/Assets:** -- The deployment must include all Phase 0 JavaScript tools from `examples/javascript-bundle/` -- All 12 Phase 0 tools must be functional in the production environment -- Sample data files must be accessible for testing tool execution -- The application uses client-side routing which requires special GitHub Pages configuration -- Deployment should serve as validation environment for stakeholder feedback - -## 2. Expected Output & Deliverables - -**Define Success:** A fully functional ToolVault application deployed on GitHub Pages with all Phase 0 tools working correctly and comprehensive testing documentation. - -**Specify Deliverables:** -- `.github/workflows/deploy.yml` - Automated deployment pipeline -- `vite.config.ts` - Production build configuration for GitHub Pages -- `404.html` - SPA routing fallback page -- `docs/deployment.md` - Comprehensive deployment documentation -- `docs/stakeholder-testing-guide.md` - User testing guide -- GitHub issue templates for feedback collection -- Working production deployment with all 12 Phase 0 tools functional -- Stakeholder demo script highlighting key capabilities -- Performance optimization and bundle size analysis - -## 3. E2E Testing Requirements - -Create comprehensive end-to-end tests to ensure the deployed service runs correctly: - -1. **Service Availability Tests** - - Verify application loads correctly at GitHub Pages URL - - Test all routes and navigation functionality - - Confirm asset loading (CSS, JS, images) without errors - -2. **Phase 0 Tool Integration Tests** - - Test tool discovery and browsing for all 12 Phase 0 tools - - Verify dynamic form generation for each tool's parameter schema - - Execute each tool with sample data and validate outputs: - - Transform tools: translate, flip-horizontal, flip-vertical - - Analysis tools: speed-series, direction-series - - Statistics tools: average-speed, speed-histogram - - Processing tools: smooth-polyline - - I/O tools: import-rep, export-rep, export-csv - - Test error handling and validation across all tools - -3. **User Workflow Tests** - - Complete user journey from tool discovery to execution - - File upload and sample data selection functionality - - Output viewing and download capabilities - - Search and filtering across tool catalog - - Responsive design validation on mobile and desktop - -## 4. Memory Bank Logging Instructions - -Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: -- A reference to Phase 1, Task 1.6 in the Implementation Plan -- A clear description of the deployment pipeline and configuration -- Production URL and deployment verification results -- Any challenges encountered with GitHub Pages or Phase 0 tool integration -- Confirmation of successful execution (all Phase 0 tools working in production with E2E test results) - -## 5. Clarification Instruction - -If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file From 1cfc62cec0bb9295163592e7718fa11ec1e82f98 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Wed, 20 Aug 2025 09:20:56 +0100 Subject: [PATCH 16/18] second pass of TAPs --- .../Task_1.1_React_TypeScript_SPA_Setup.md | 72 ++++++++++ ..._1.2_Load_JavaScript_Bundle_Integration.md | 75 +++++++++++ ...3_Tool_Discovery_and_Browsing_Interface.md | 104 +++++++++++++++ .../Task_1.4_Metadata_Driven_UI_Generation.md | 75 +++++++++++ .../Task_1.5_Tool_Execution_Interface.md | 88 +++++++++++++ .../tasks/Task_1.6_GitHub_Pages_Deployment.md | 124 ++++++++++++++++++ 6 files changed, 538 insertions(+) create mode 100644 prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md create mode 100644 prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md create mode 100644 prompts/tasks/Task_1.3_Tool_Discovery_and_Browsing_Interface.md create mode 100644 prompts/tasks/Task_1.4_Metadata_Driven_UI_Generation.md create mode 100644 prompts/tasks/Task_1.5_Tool_Execution_Interface.md create mode 100644 prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md diff --git a/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md b/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md new file mode 100644 index 0000000..bf89ac7 --- /dev/null +++ b/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md @@ -0,0 +1,72 @@ +# APM Task Assignment: React/TypeScript SPA Setup + +## 1. Agent Role & APM Context + +You are activated as an Implementation Agent within the Agentic Project Management (APM) framework for the ToolVault project. Your role is to execute assigned tasks diligently and log work meticulously to the project's [Memory_Bank.md](../../Memory_Bank.md) file. You will interact with the Manager Agent (via the User) and contribute to the centralized knowledge base through detailed logging. + +## 2. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.1` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Establish the core frontend application infrastructure using modern web development tools to support the JavaScript tool bundle created in Phase 0. + +**Detailed Action Steps:** + +1. **Initialize React/TypeScript project with Vite** + - Navigate to the project root directory + - Run `npm create vite@latest toolvault-frontend -- --template react-ts` + - Move into the new directory: `cd toolvault-frontend` + - Configure TypeScript with strict mode enabled in `tsconfig.json` + - Set up project directory structure: + - `/src/components` - React components + - `/src/services` - Tool loading and execution services + - `/src/types` - TypeScript interface definitions + - `/src/utils` - Utility functions + - Configure Vite for development and production builds with proper base path settings + +2. **Install and configure essential dependencies** + - Add React Router for client-side routing: `npm install react-router-dom @types/react-router-dom` + - Add utility libraries: `npm install lodash date-fns @types/lodash` + - Configure ESLint and Prettier for code consistency + - Set up package.json scripts for development workflow + +3. **Set up development environment** + - Create `.vscode/settings.json` and `.vscode/extensions.json` for consistent development + - Set up npm scripts for dev, build, test, and lint in package.json + - Create basic folder structure and initial components: + - `src/App.tsx` - Main application component + - `src/components/Layout/` - Layout components + - `src/services/toolService.ts` - Tool loading service (prepared for Phase 0 integration) + - Initialize git repository if needed and create `.gitignore` + +**Provide Necessary Context/Assets:** +- The application must be designed to integrate with the JavaScript tool bundle from `examples/javascript-bundle/` +- Reference `examples/javascript-bundle/index.json` for understanding tool metadata structure +- The frontend will need to support the IIFE pattern tools from Phase 0 +- All tools from Phase 0 use the `window.ToolVault.tools` namespace +- Do not include axios - use native fetch() and direct imports for bundle integration + +## 3. Expected Output & Deliverables + +**Define Success:** A fully functional React/TypeScript application with Vite build system that can serve as the foundation for integrating Phase 0 JavaScript tools. + +**Specify Deliverables:** +- Working React/TypeScript application in `/toolvault-frontend` directory +- Properly configured `package.json` with required dependencies (no axios) +- TypeScript configuration with strict mode enabled +- ESLint and Prettier configuration files +- Basic component structure and routing setup +- Development scripts that run without errors: `npm run dev`, `npm run build`, `npm run lint` + +## 4. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.1 in the Implementation Plan +- A clear description of the React/TypeScript setup actions taken +- Key configuration decisions made (TypeScript settings, dependencies chosen) +- Any challenges encountered during setup +- Confirmation of successful execution (dev server running, build succeeds) + +## 5. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md b/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md new file mode 100644 index 0000000..d72b62e --- /dev/null +++ b/prompts/tasks/Task_1.2_Load_JavaScript_Bundle_Integration.md @@ -0,0 +1,75 @@ +# APM Task Assignment: Load Standard Bundle from Phase 0 + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.2` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Integrate the JavaScript toolbox created in Phase 0 as the standard tool bundle for the React frontend. + +**Detailed Action Steps:** + +1. **Create bundle loading service** + - Reference `/examples/javascript-bundle/index.json` as the primary bundle source + - Implement `src/services/bundleLoader.ts` to fetch and parse tool metadata from Phase 0 + - Create TypeScript interfaces in `src/types/tools.ts` matching Phase 0 tool definitions: + - Interface for tool metadata structure from `index.json` + - Parameter schema interfaces (number, string, boolean, enum types) + - Input/output type definitions (Feature, FeatureCollection, etc.) + - Handle loading errors with fallback mechanisms and user-friendly error messages + - Support both development (local file) and production (bundled) loading scenarios + - Use native fetch() or direct imports instead of external HTTP libraries + +2. **Implement tool registry system** + - Create `src/services/toolRegistry.ts` for in-memory registry of Phase 0 bundle tools + - Implement filtering capabilities by: + - Category (Transform, Analysis, Statistics, Processing, I/O - note: these should not be hard-coded, but derive from the metadata) + - Runtime type ("javascript") + - Search terms across tool names and descriptions + - Implement tool metadata caching for performance optimization + - Add tool availability status tracking and health checks + - Create methods for: `getTools()`, `getToolById()`, `getToolsByCategory()`, `searchTools()` + +3. **Load Phase 0 JavaScript tools** + - Implement dynamic script loading in `src/services/scriptLoader.ts` using IIFE pattern from Phase 0 + - Verify tool function availability in `window.ToolVault.tools` namespace after loading + - Handle script loading errors gracefully with retry mechanisms + - Test integration with all 12 tools from Phase 0: + - Transform: translate, flip-horizontal, flip-vertical + - Analysis: speed-series, direction-series + - Statistics: average-speed, speed-histogram + - Processing: smooth-polyline + - I/O: import-rep, export-rep, export-csv + - Create tool execution wrapper with error handling and validation + +**Provide Necessary Context/Assets:** +- Phase 0 tools are implemented using IIFE pattern and register in `window.ToolVault.tools` namespace +- The `examples/javascript-bundle/index.json` contains complete metadata with runtime field = "javascript" +- All Phase 0 tools expect specific input formats: GeoJSON Feature/FeatureCollection, LineString, etc. +- Phase 0 tools include comprehensive parameter schemas with defaults and validation rules +- Reference ADR-013 for JavaScript tool implementation specification + +## 2. Expected Output & Deliverables + +**Define Success:** A fully functional bundle loading system that can dynamically load and execute all Phase 0 JavaScript tools from the frontend. + +**Specify Deliverables:** +- `src/services/bundleLoader.ts` - Bundle metadata loading service +- `src/services/toolRegistry.ts` - Tool registry and filtering system +- `src/services/scriptLoader.ts` - Dynamic script loading for IIFE tools +- `src/types/tools.ts` - TypeScript interfaces for Phase 0 tool structure +- Successful loading and execution test of all 12 Phase 0 tools +- Error handling for bundle loading failures +- Tool availability verification system + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.2 in the Implementation Plan +- A clear description of the bundle integration implementation +- Key decisions made for tool loading architecture +- Any challenges encountered with Phase 0 tool integration +- Confirmation of successful execution (all 12 Phase 0 tools loading and executing correctly) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.3_Tool_Discovery_and_Browsing_Interface.md b/prompts/tasks/Task_1.3_Tool_Discovery_and_Browsing_Interface.md new file mode 100644 index 0000000..6dbbae5 --- /dev/null +++ b/prompts/tasks/Task_1.3_Tool_Discovery_and_Browsing_Interface.md @@ -0,0 +1,104 @@ +# APM Task Assignment: Tool Discovery and Browsing Interface + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.3` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Build intuitive interface for browsing and discovering Phase 0 JavaScript tools with comprehensive search and filtering capabilities. + +**Detailed Action Steps:** + +1. **Parse tool metadata from Phase 0 index.json** + - Load and validate `examples/javascript-bundle/index.json` structure with runtime field + - Create TypeScript interfaces in `src/types/metadata.ts` for: + - Tool definitions with parameter schemas + - Parameter types with constraints and validation rules + - Input/output type specifications + - Implement metadata parsing service in `src/services/metadataParser.ts` + - Handle metadata parsing errors and validation with detailed error messages + - Support schema validation against expected tool definition structure + +2. **Implement tool browser component** + - Create `src/components/ToolBrowser/` component system: + - `ToolBrowser.tsx` - Main browser container with grid/list view toggle + - `ToolCard.tsx` - Individual tool metadata display cards + - `CategoryFilter.tsx` - Category-based filtering component (derive categories from metadata, don't hard-code) + - `ViewControls.tsx` - View mode and sorting controls + - Display Phase 0 tools with metadata cards showing: + - Tool name, description, and category from index.json + - Parameter count and complexity indicators + - Runtime type ("javascript") and version information + - Input/output type specifications + - Implement responsive design supporting grid (desktop) and list (mobile) views + - Add sorting options: alphabetical, category, complexity + +3. **Add search and filtering capabilities** + - Implement `src/services/toolSearch.ts` search service: + - Text search across Phase 0 tool names and descriptions + - Search indexing for performance with tool metadata + - Search result ranking and relevance scoring + - Search history and recent searches tracking + - Create `src/components/SearchInterface/` components: + - `SearchBar.tsx` - Main search input with autocomplete + - `SearchFilters.tsx` - Advanced filtering controls + - `FilterChips.tsx` - Active filter display and removal + - Support filtering by: + - Category matching Phase 0 tool organization (derived from metadata) + - Runtime type filtering (JavaScript initially, extensible) + - Parameter count ranges (simple vs complex tools) + - Input/output type requirements + - Implement clear/reset filters functionality with state persistence + - Add search suggestions based on Phase 0 tool metadata + +4. **Design tool detail view** + - Create `src/components/ToolDetail/` detailed view components: + - `ToolDetail.tsx` - Complete tool information display + - `ParameterSpecs.tsx` - Parameter specification viewer + - `IOSpecs.tsx` - Input/output type specifications + - `ExampleViewer.tsx` - Tool usage examples from index.json + - Display complete tool information from Phase 0 metadata: + - Full description with usage context + - Detailed parameter specifications with types and defaults + - Input/output requirements and format specifications + - Example usage scenarios from tool metadata + - Version information and commit history + - Add "Try Tool" button to navigate to execution interface + - Include breadcrumb navigation and back to browse functionality + - Support deep linking to specific tool detail pages + +**Provide Necessary Context/Assets:** +- All 12 Phase 0 tools have complete metadata in `examples/javascript-bundle/index.json` +- Each tool includes examples array with parameter sets for demonstration +- Tools span 5 categories with varying complexity levels +- Search must be fast and responsive for tool discovery workflow +- Design should accommodate future tool bundles beyond Phase 0 + +## 2. Expected Output & Deliverables + +**Define Success:** An intuitive and efficient tool discovery interface that allows users to easily find and learn about Phase 0 tools before execution. + +**Specify Deliverables:** +- `src/services/metadataParser.ts` - Metadata parsing and validation service +- `src/components/ToolBrowser/` - Complete tool browsing interface +- `src/components/SearchInterface/` - Comprehensive search and filtering system +- `src/components/ToolDetail/` - Detailed tool information display +- `src/services/toolSearch.ts` - Search service with indexing and ranking +- `src/types/metadata.ts` - TypeScript interfaces for tool metadata +- Responsive design supporting desktop and mobile browsing +- Working search across all Phase 0 tool metadata +- Category filtering derived from metadata (not hard-coded) +- Deep linking support for tool detail pages +- State management for filters and search persistence + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.3 in the Implementation Plan +- A clear description of the tool discovery interface implementation +- Key UX decisions for search and browsing workflow +- Any challenges encountered with metadata parsing or search performance +- Confirmation of successful execution (all Phase 0 tools discoverable and browsable) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.4_Metadata_Driven_UI_Generation.md b/prompts/tasks/Task_1.4_Metadata_Driven_UI_Generation.md new file mode 100644 index 0000000..ed81aed --- /dev/null +++ b/prompts/tasks/Task_1.4_Metadata_Driven_UI_Generation.md @@ -0,0 +1,75 @@ +# APM Task Assignment: Metadata-Driven UI from index.json + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.4` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Create dynamic UI components that generate interfaces directly from Phase 0 tool metadata, focusing on dynamic form generation and input/output rendering systems. + +**Detailed Action Steps:** + +1. **Implement dynamic form generation** + - Take note of UI requirements in `docs/ui/readme.md` + - Create `src/components/DynamicForm/` component suite: + - `ParameterForm.tsx` - Main form component + - `ParameterField.tsx` - Individual parameter input fields + - `ParameterValidation.ts` - Validation logic service + - Generate forms dynamically based on Phase 0 parameter schemas supporting: + - Number fields with min/max validation and step controls + - String fields with pattern validation and text areas + - Boolean fields with checkbox/toggle components + - Enum fields with dropdown/radio button selection + - Default value population from Phase 0 tool metadata + - Implement form state management using React hooks with real-time validation + - Add parameter help text display and validation error feedback + - Support parameter dependency handling and conditional field display + +2. **Create input/output rendering system** + - Develop `src/components/IOViewer/` component system: + - `InputViewer.tsx` - Input data display and selection + - `OutputViewer.tsx` - Output data rendering and visualization + - `IOTabs.tsx` - Tabbed input/output viewer (Raw, Preview, Download) + - `JSONRenderer.tsx` - Syntax highlighted JSON display + - `FileHandler.tsx` - File input/output generation and download + - Support input/output formats from Phase 0 tools: + - GeoJSON (Feature, FeatureCollection) with formatted display + - JSON objects and arrays with collapsible tree view + - String outputs (CSV, REP format) with text formatting + - File inputs/outputs with upload/download trigger functionality + - Implement syntax highlighting using `react-syntax-highlighter` + - Handle large input/output rendering with pagination and virtualization + - Add input/output copying functionality and format conversion options + +**Provide Necessary Context/Assets:** +- Phase 0 tools return diverse input/output types: GeoJSON, JSON objects, strings, arrays +- All parameter definitions include validation rules, defaults, and help text in index.json +- The UI must handle all 12 Phase 0 tool parameter schemas dynamically +- Reference ADR-001 for metadata-driven UI architecture principles +- Input/output viewer must prepare for LeafletJS integration (Task 2.1) for spatial data +- Check for UI requirements documentation in `docs/ui/readme.md` + +## 2. Expected Output & Deliverables + +**Define Success:** A complete metadata-driven UI system that dynamically generates forms and input/output viewers for all Phase 0 tools without hard-coded tool-specific components. + +**Specify Deliverables:** +- `src/components/DynamicForm/` - Complete dynamic form generation system +- `src/components/IOViewer/` - Multi-format input/output rendering system +- Working form generation for all 12 Phase 0 tool parameter schemas +- Input/output rendering supporting all Phase 0 tool formats +- Form validation and error handling system +- Responsive design supporting different screen sizes +- Syntax highlighting and data visualization components + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.4 in the Implementation Plan +- A clear description of the metadata-driven UI implementation +- Key architectural decisions for dynamic form and input/output rendering +- Any challenges encountered with complex parameter schemas or UI requirements +- Confirmation of successful execution (forms and I/O viewers generated correctly for all 12 Phase 0 tools) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.5_Tool_Execution_Interface.md b/prompts/tasks/Task_1.5_Tool_Execution_Interface.md new file mode 100644 index 0000000..f564522 --- /dev/null +++ b/prompts/tasks/Task_1.5_Tool_Execution_Interface.md @@ -0,0 +1,88 @@ +# APM Task Assignment: Dynamic Input Forms and Output Renderers + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.5` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Create the execution interface that works with Phase 0 tool specifications, enabling users to run tools with proper input handling and output display. + +**Detailed Action Steps:** + +1. **Build tool execution page** + - Create `src/pages/ToolExecution.tsx` main execution interface: + - Display selected tool information and dynamically generated parameter forms + - Integrate sample data selection from Phase 0 for quick testing + - Implement real-time parameter validation based on Phase 0 schemas + - Add execute button with loading states, progress indication, and cancellation + - Implement `src/components/InputSelection/` components: + - `InputDataSelector.tsx` - Choose between file upload, sample data, or manual input + - `SampleDataPicker.tsx` - Select from Phase 0 sample data (sample-track.geojson, sample-features.geojson) + - `FileUploader.tsx` - Handle GeoJSON, JSON, and text file uploads + - `DataPreview.tsx` - Preview input data before tool execution + - Add input validation matching Phase 0 tool input_types requirements + - Support routing from tool browser with tool ID parameter + +2. **Implement JavaScript tool execution engine** + - Create `src/services/toolExecutor.ts` execution service: + - Execute Phase 0 tools using `window.ToolVault.tools[toolId](input, params)` pattern + - Handle synchronous tool execution with proper error catching and timeout + - Support different input types from Phase 0: Feature, FeatureCollection, Point, LineString, Polygon + - Implement execution logging and performance monitoring + - Add comprehensive error handling: + - Parameter validation errors with specific field feedback + - Tool execution errors with user-friendly messages + - Input format validation and conversion errors + - Timeout handling for long-running operations + - Create execution history tracking using localStorage initially + - Support execution cancellation and cleanup + +3. **Create results display system** + - Develop `src/components/ExecutionResults/` result display system: + - `ResultsContainer.tsx` - Multi-tab input/output viewer container + - `RawOutput.tsx` - Raw JSON/text output with syntax highlighting + - `FormattedOutput.tsx` - Structured display for specific output types + - `DownloadOutput.tsx` - File download functionality for string outputs + - Support Phase 0 tool input/output types: + - GeoJSON (Feature, FeatureCollection) with formatted JSON display + - JSON objects and arrays with collapsible tree structures + - String outputs (CSV, REP format) with proper formatting + - Array outputs (time series) with tabular display + - Prepare spatial output tab placeholder for LeafletJS integration (Task 2.1) + - Add execution metadata display: execution time, tool version, parameter summary + - Implement output comparison functionality for multiple executions + - Support output export and sharing capabilities + +**Provide Necessary Context/Assets:** +- Phase 0 sample data files: `examples/javascript-bundle/data/sample-track.geojson`, `sample-features.geojson` +- All Phase 0 tools expect specific input formats defined in their input_types metadata +- Tools return diverse outputs: GeoJSON objects, JSON arrays, formatted strings +- Phase 0 tools are synchronous and execute in browser context via window.ToolVault.tools +- Execution interface must handle all 12 Phase 0 tools with their unique parameter requirements + +## 2. Expected Output & Deliverables + +**Define Success:** A complete tool execution interface that allows users to run any Phase 0 tool with appropriate inputs and view results in multiple formats. + +**Specify Deliverables:** +- `src/pages/ToolExecution.tsx` - Main tool execution page +- `src/components/InputSelection/` - Complete input handling system +- `src/components/ExecutionResults/` - Multi-format results display +- `src/services/toolExecutor.ts` - Tool execution service with error handling +- Working execution for all 12 Phase 0 tools with sample data +- Parameter validation and error feedback system +- Execution history tracking and result comparison +- File upload and download functionality +- Loading states and progress indication + +## 3. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.5 in the Implementation Plan +- A clear description of the tool execution interface implementation +- Key decisions for input handling and output rendering architecture +- Any challenges encountered with Phase 0 tool execution or error handling +- Confirmation of successful execution (all 12 Phase 0 tools executable with proper I/O handling) + +## 4. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file diff --git a/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md b/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md new file mode 100644 index 0000000..fcb4ce8 --- /dev/null +++ b/prompts/tasks/Task_1.6_GitHub_Pages_Deployment.md @@ -0,0 +1,124 @@ +# APM Task Assignment: GitHub Pages Deployment + +## 1. Task Assignment + +**Reference Implementation Plan:** This assignment corresponds to `Phase 1, Task 1.6` in the [Implementation_Plan.md](../../Implementation_Plan.md). + +**Objective:** Deploy the working application with Phase 0 tools for stakeholder review, creating a production-ready instance for testing and validation. + +**Detailed Action Steps:** + +1. **Configure deployment pipeline** + - Create `.github/workflows/deploy.yml` GitHub Actions workflow: + - Automated build triggers on main branch push and pull requests + - Node.js setup with proper version specification (18.x or 20.x) + - Install dependencies and run build process + - Include Phase 0 bundle assets in build artifact + - Configure Vite build for GitHub Pages deployment: + - Set proper base path in `vite.config.ts` for GitHub Pages subdirectory + - Ensure all Phase 0 JavaScript bundle files are included in build output + - Configure asset handling for `examples/javascript-bundle/` integration + - Optimize bundle size with code splitting and tree shaking + - Handle client-side routing compatibility: + - Create `404.html` fallback for React Router navigation + - Configure proper redirect handling for SPA routing + - Test routing works correctly in GitHub Pages environment + +2. **Deploy to GitHub Pages** + - Configure repository settings for GitHub Pages: + - Enable GitHub Pages from GitHub Actions deployment source + - Set custom domain if available or use default github.io subdomain + - Configure HTTPS and security settings + - Ensure Phase 0 JavaScript bundle accessibility: + - Verify all tool scripts load correctly in production environment + - Test dynamic script loading from `examples/javascript-bundle/tools/` + - Confirm `index.json` metadata loading and parsing + - Validate CORS settings for cross-origin resource loading + - Create production deployment verification: + - Test all 12 Phase 0 tools execute correctly in production + - Verify tool parameter handling and output rendering + - Test file upload/download functionality + - Confirm responsive design works across devices + +3. **Create deployment documentation and testing guides** + - Create `docs/deployment.md` documentation: + - Step-by-step deployment process including Phase 0 bundle integration + - Troubleshooting guide for common deployment issues + - Environment configuration and build optimization notes + - Instructions for updating deployed version with new Phase 0 tools + - Create `docs/stakeholder-testing-guide.md`: + - User guide for stakeholder testing of Phase 0 tool functionality + - Test scenarios covering all 5 tool categories (Transform, Analysis, Statistics, Processing, I/O) + - Expected outputs and validation criteria for each tool type + - Known limitations and future enhancement roadmap + - Set up feedback collection mechanism: + - Create GitHub issue templates for bug reports and feature requests + - Add feedback link in application header/footer + - Configure issue labels for different types of feedback (UI, tools, performance) + - Prepare stakeholder demo script: + - Highlight Phase 0 tool capabilities with specific examples + - Demonstrate tool discovery, execution, and output rendering workflow + - Show metadata-driven UI generation and dynamic form creation + - Present tool execution with sample data and real-world scenarios + +**Provide Necessary Context/Assets:** +- The deployment must include all Phase 0 JavaScript tools from `examples/javascript-bundle/` +- All 12 Phase 0 tools must be functional in the production environment +- Sample data files must be accessible for testing tool execution +- The application uses client-side routing which requires special GitHub Pages configuration +- Deployment should serve as validation environment for stakeholder feedback + +## 2. Expected Output & Deliverables + +**Define Success:** A fully functional ToolVault application deployed on GitHub Pages with all Phase 0 tools working correctly and comprehensive testing documentation. + +**Specify Deliverables:** +- `.github/workflows/deploy.yml` - Automated deployment pipeline +- `vite.config.ts` - Production build configuration for GitHub Pages +- `404.html` - SPA routing fallback page +- `docs/deployment.md` - Comprehensive deployment documentation +- `docs/stakeholder-testing-guide.md` - User testing guide +- GitHub issue templates for feedback collection +- Working production deployment with all 12 Phase 0 tools functional +- Stakeholder demo script highlighting key capabilities +- Performance optimization and bundle size analysis + +## 3. E2E Testing Requirements + +Create comprehensive end-to-end tests to ensure the deployed service runs correctly: + +1. **Service Availability Tests** + - Verify application loads correctly at GitHub Pages URL + - Test all routes and navigation functionality + - Confirm asset loading (CSS, JS, images) without errors + +2. **Phase 0 Tool Integration Tests** + - Test tool discovery and browsing for all 12 Phase 0 tools + - Verify dynamic form generation for each tool's parameter schema + - Execute each tool with sample data and validate outputs: + - Transform tools: translate, flip-horizontal, flip-vertical + - Analysis tools: speed-series, direction-series + - Statistics tools: average-speed, speed-histogram + - Processing tools: smooth-polyline + - I/O tools: import-rep, export-rep, export-csv + - Test error handling and validation across all tools + +3. **User Workflow Tests** + - Complete user journey from tool discovery to execution + - File upload and sample data selection functionality + - Output viewing and download capabilities + - Search and filtering across tool catalog + - Responsive design validation on mobile and desktop + +## 4. Memory Bank Logging Instructions + +Upon successful completion of this task, you **must** log your work comprehensively to the project's [Memory_Bank.md](../../Memory_Bank.md) file. Adhere strictly to the established logging format. Ensure your log includes: +- A reference to Phase 1, Task 1.6 in the Implementation Plan +- A clear description of the deployment pipeline and configuration +- Production URL and deployment verification results +- Any challenges encountered with GitHub Pages or Phase 0 tool integration +- Confirmation of successful execution (all Phase 0 tools working in production with E2E test results) + +## 5. Clarification Instruction + +If any part of this task assignment is unclear, please state your specific questions before proceeding. \ No newline at end of file From f7711d5eb11d901317efb41e1da9ef97dfec0954 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Wed, 20 Aug 2025 09:24:45 +0100 Subject: [PATCH 17/18] feat: add Playwright E2E testing and strict TypeScript linting with Husky hooks --- Implementation_Plan.md | 5 ++++- .../Task_1.1_React_TypeScript_SPA_Setup.md | 20 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Implementation_Plan.md b/Implementation_Plan.md index 837debf..5e18bac 100644 --- a/Implementation_Plan.md +++ b/Implementation_Plan.md @@ -154,11 +154,14 @@ Objective: Establish the core frontend application infrastructure using modern w - Add React Router for client-side routing - Install Axios for HTTP requests - Add utility libraries: lodash, date-fns - - Configure ESLint and Prettier for code consistency + - Configure ESLint and Prettier for code consistency. ESLint should do strict TS checking, and not allow use of `any`. TS linting should be struct, with husky hook running on pre-push. + - Add `playwright` and `@playwright/test`, introduce e2e test to verify bare app functioning and present. e2e tests should run on pre-push 3. Set up development environment. - Configure VS Code settings and extensions recommendations - Set up npm scripts for dev, build, test, and lint - Create basic folder structure and initial components +4. Setup test environment. + - Introduce e2e test to verify bare app functioning and present. ### Task 1.2 - Agent_Frontend_Lead: Load Standard Bundle from Phase 0 Objective: Integrate the JavaScript toolbox created in Phase 0 as the standard tool bundle. diff --git a/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md b/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md index bf89ac7..19e36f2 100644 --- a/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md +++ b/prompts/tasks/Task_1.1_React_TypeScript_SPA_Setup.md @@ -27,11 +27,15 @@ You are activated as an Implementation Agent within the Agentic Project Manageme 2. **Install and configure essential dependencies** - Add React Router for client-side routing: `npm install react-router-dom @types/react-router-dom` - Add utility libraries: `npm install lodash date-fns @types/lodash` - - Configure ESLint and Prettier for code consistency + - Configure ESLint and Prettier for code consistency: + - ESLint should do strict TypeScript checking and not allow use of `any` + - TypeScript linting should be strict with husky hook running on pre-push + - Add Playwright for E2E testing: `npm install playwright @playwright/test --save-dev` (note only need to support Chrome browser tests) + - Introduce E2E test to verify bare app functioning and present - E2E tests should run on pre-push - Set up package.json scripts for development workflow 3. **Set up development environment** - - Create `.vscode/settings.json` and `.vscode/extensions.json` for consistent development + - Configure VS Code settings and extensions recommendations - Set up npm scripts for dev, build, test, and lint in package.json - Create basic folder structure and initial components: - `src/App.tsx` - Main application component @@ -39,6 +43,12 @@ You are activated as an Implementation Agent within the Agentic Project Manageme - `src/services/toolService.ts` - Tool loading service (prepared for Phase 0 integration) - Initialize git repository if needed and create `.gitignore` +4. **Setup test environment** + - Introduce E2E test to verify bare app functioning and present + - Configure Playwright test environment for browser automation + - Set up husky pre-push hooks to run both TypeScript linting and E2E tests + - Create initial E2E test suite to validate basic application functionality + **Provide Necessary Context/Assets:** - The application must be designed to integrate with the JavaScript tool bundle from `examples/javascript-bundle/` - Reference `examples/javascript-bundle/index.json` for understanding tool metadata structure @@ -54,9 +64,11 @@ You are activated as an Implementation Agent within the Agentic Project Manageme - Working React/TypeScript application in `/toolvault-frontend` directory - Properly configured `package.json` with required dependencies (no axios) - TypeScript configuration with strict mode enabled -- ESLint and Prettier configuration files +- ESLint and Prettier configuration files with strict TypeScript checking (no `any` allowed) +- Playwright E2E testing setup with initial test suite +- Husky pre-push hooks configured for TypeScript linting and E2E tests - Basic component structure and routing setup -- Development scripts that run without errors: `npm run dev`, `npm run build`, `npm run lint` +- Development scripts that run without errors: `npm run dev`, `npm run build`, `npm run lint`, `npm run test:e2e` ## 4. Memory Bank Logging Instructions From 7b8fd11aeab9f264159a83b85836b3155bd74284 Mon Sep 17 00:00:00 2001 From: Ian Mayo Date: Wed, 20 Aug 2025 09:27:16 +0100 Subject: [PATCH 18/18] drop premature CI tasks --- .github/workflows/ci.yml | 122 ----------------------------- .github/workflows/deploy-pages.yml | 35 --------- 2 files changed, 157 deletions(-) delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/deploy-pages.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index a6b1f90..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,122 +0,0 @@ -name: CI/CD Pipeline - -on: - push: - branches: [ main, develop ] - pull_request: - branches: [ main ] - -jobs: - test: - name: Test & Build - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 8 - - - name: Install dependencies - run: | - cd client - pnpm install --frozen-lockfile - - - name: Generate types - run: | - cd client - pnpm generate-types - - - name: Lint code - run: | - cd client - pnpm lint - - - name: Type check - run: | - cd client - pnpm typecheck - - - name: Run unit tests - run: | - cd client - pnpm test:coverage - - - name: Build project - run: | - cd client - pnpm build - - - name: Upload test coverage - uses: actions/upload-artifact@v4 - with: - name: coverage-report - path: client/coverage/ - retention-days: 30 - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build-artifacts - path: client/dist/ - retention-days: 7 - - e2e: - name: E2E Tests - runs-on: ubuntu-latest - needs: test - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 8 - - - name: Install dependencies - run: | - cd client - pnpm install --frozen-lockfile - - - name: Install Playwright browsers - run: | - cd client - npx playwright install --with-deps - - - name: Generate types - run: | - cd client - pnpm generate-types - - - name: Build project - run: | - cd client - pnpm build - - - name: Run Playwright tests - run: | - cd client - pnpm test:e2e - - - name: Upload Playwright report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: client/playwright-report/ - retention-days: 30 \ No newline at end of file diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml deleted file mode 100644 index 74780c2..0000000 --- a/.github/workflows/deploy-pages.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Build and Deploy to GitHub Pages - -on: - push: - branches: [main] - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'pnpm' - - name: Install pnpm - run: npm install -g pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm test - - run: pnpm build - - uses: actions/upload-pages-artifact@v3 - with: - path: ./client/dist - - deploy: - needs: build - runs-on: ubuntu-latest - permissions: - pages: write - id-token: write - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v3