diff --git a/.cursor/rules/README.md b/.cursor/rules/README.md new file mode 100644 index 00000000..ae4db587 --- /dev/null +++ b/.cursor/rules/README.md @@ -0,0 +1,306 @@ +# .cursor Rules Organization + +This directory contains all the rules and guidelines for AI assistants working +with the TimeSafari project. + +## Directory Structure + +### **`core/`** - Core Principles and Context + +Core rules that apply to all AI interactions and provide fundamental context. + +- **`base_context.mdc`** - Human competence first principles and interaction guidelines +- **`harbor_pilot_universal.mdc`** - Technical guide creation and investigation rules +- **`less_complex.mdc`** - Minimalist solution principle and complexity guidelines + +### **`development/`** - Development Practices and Standards + +Rules for software development, coding standards, and development workflows. + +- **`software_development.mdc`** - Core development principles and evidence requirements +- **`type_safety_guide.mdc`** - TypeScript type safety guidelines and best practices +- **`development_guide.mdc`** - Development environment setup and standards +- **`logging_standards.mdc`** - Logging implementation standards and rules +- **`logging_migration.mdc`** - Migration from console.* to structured logging +- **`time.mdc`** - Time handling principles and UTC standards +- **`time_examples.mdc`** - Practical time implementation examples +- **`time_implementation.mdc`** - Detailed time implementation guidelines +- **`realistic_time_estimation.mdc`** - Time estimation framework and principles +- **`planning_examples.mdc`** - Planning examples and best practices +- **`complexity_assessment.mdc`** - Complexity evaluation and assessment +- **`dependency_management.mdc`** - Dependency management and version control +- **`asset_configuration.mdc`** - Asset configuration and build integration +- **`research_diagnostic.mdc`** - Research and investigation workflows +- **`investigation_report_example.mdc`** - Investigation report templates and examples +- **`historical_comment_management.mdc`** - Historical comment transformation rules +- **`historical_comment_patterns.mdc`** - Comment transformation patterns and examples + +### **`architecture/`** - Architecture and Design Patterns + +Rules for architectural decisions, patterns, and system design. + +- **`build_architecture_guard.mdc`** - Build system protection and change levels +- **`build_validation.mdc`** - Build validation procedures and testing +- **`build_testing.mdc`** - Build testing requirements and feedback collection + +### **`app/`** - Application-Specific Rules + +Rules specific to the TimeSafari application and its architecture. + +- **`timesafari.mdc`** - Core application context and principles +- **`timesafari_platforms.mdc`** - Platform-specific implementation guidelines +- **`timesafari_development.mdc`** - TimeSafari development workflow +- **`architectural_decision_record.mdc`** - ADR creation and management +- **`architectural_implementation.mdc`** - Architecture implementation guidelines +- **`architectural_patterns.mdc`** - Architectural patterns and examples +- **`architectural_examples.mdc`** - Architecture examples and testing + +### **`database/`** - Database and Data Management + +Rules for database operations, migrations, and data handling. + +- **`absurd-sql.mdc`** - Absurd SQL implementation and worker thread setup +- **`legacy_dexie.mdc`** - Legacy Dexie migration guidelines + +### **`workflow/`** - Process and Workflow Management + +Rules for development workflows, version control, and process management. + +- **`version_control.mdc`** - Version control principles and commit guidelines +- **`version_sync.mdc`** - Version synchronization and changelog management +- **`commit_messages.mdc`** - Commit message format and conventions + +### **`features/** - Feature-Specific Implementations + +Rules for implementing specific features across platforms. + +- **`camera-implementation.mdc`** - Camera feature implementation overview +- **`camera_technical.mdc`** - Technical camera implementation details +- **`camera_platforms.mdc`** - Platform-specific camera implementation + +### **`docs/`** - Documentation Standards + +Rules for creating and maintaining documentation. + +- **`markdown_core.mdc`** - Core markdown formatting standards +- **`markdown_templates.mdc`** - Document templates and examples +- **`markdown_workflow.mdc`** - Markdown validation and workflow +- **`documentation.mdc`** - Documentation generation principles +- **`meta_rule_usage_guide.md`** - How to use meta-rules in practice + +### **`templates/`** - Templates and Examples + +Template files and examples for various documentation types. + +- **`adr_template.mdc`** - Architectural Decision Record template + +### **Meta-Rules** - Workflow Bundling + +High-level meta-rules that bundle related sub-rules for specific workflows. + +- **`meta_core_always_on.mdc`** - Core rules that apply to every single prompt +- **`meta_documentation.mdc`** - Documentation writing and education workflow +- **`meta_feature_planning.mdc`** - Feature planning workflow bundling +- **`meta_bug_diagnosis.mdc`** - Bug investigation workflow bundling +- **`meta_bug_fixing.mdc`** - Bug fix implementation workflow bundling +- **`meta_feature_implementation.mdc`** - Feature implementation workflow bundling +- **`meta_research.mdc`** - Investigation and research workflow bundling + +### **Workflow State Management** + +The project uses a sophisticated workflow state management system to ensure systematic development processes and maintain code quality across all phases of development. + +#### **Workflow State System** + +The workflow state is managed through `.cursor/rules/.workflow_state.json` and enforces different modes with specific constraints. The system automatically tracks workflow progression and maintains a complete history of mode transitions. + +**Available Modes**: +- **`diagnosis`** - Investigation and analysis phase (read-only) +- **`fixing`** - Implementation and bug fixing phase (full access) +- **`planning`** - Design and architecture phase (design only) +- **`research`** - Investigation and research phase (investigation only) +- **`documentation`** - Documentation writing phase (writing only) + +**Mode Constraints**: +```json +{ + "diagnosis": { + "mode": "read_only", + "forbidden": ["modify", "create", "build", "commit"], + "allowed": ["read", "search", "analyze", "document"] + }, + "fixing": { + "mode": "implementation", + "forbidden": [], + "allowed": ["modify", "create", "build", "commit", "test"] + } +} +``` + +**Workflow History Tracking**: + +The system automatically maintains a `workflowHistory` array that records all mode transitions and meta-rule invocations: + +```json +{ + "workflowHistory": [ + { + "mode": "research", + "invoked": "meta_core_always_on.mdc", + "timestamp": "2025-08-25T02:14:37Z" + }, + { + "mode": "diagnosis", + "invoked": "meta_bug_diagnosis.mdc", + "timestamp": "2025-08-25T02:14:37Z" + } + ] +} +``` + +**History Entry Format**: +- **`mode`**: The workflow mode that was activated +- **`invoked`**: The specific meta-rule that triggered the mode change +- **`timestamp`**: UTC timestamp when the mode transition occurred + +**History Purpose**: +- **Workflow Continuity**: Track progression through development phases +- **Meta-Rule Usage**: Monitor which rules are invoked and when +- **Temporal Context**: Maintain chronological order of workflow changes +- **State Persistence**: Preserve workflow history across development sessions +- **Debugging Support**: Help diagnose workflow state issues +- **Process Analysis**: Understand development patterns and meta-rule effectiveness + +#### **Commit Override System** + +The workflow includes a flexible commit override mechanism that allows commits on demand while maintaining workflow integrity: + +```json +{ + "overrides": { + "commit": { + "allowed": true, + "requires_override": true, + "override_reason": "user_requested" + } + } +} +``` + +**Override Benefits**: +- ✅ **Investigation Commits**: Document findings during diagnosis phases +- ✅ **Work-in-Progress**: Commit partial solutions during complex investigations +- ✅ **Emergency Fixes**: Commit critical fixes without mode transitions +- ✅ **Flexible Workflow**: Maintain systematic approach while accommodating real needs + +**Override Limitations**: +- ❌ **Does NOT bypass**: Version control rules, commit message standards, or security requirements +- ❌ **Does NOT bypass**: Code quality standards, testing requirements, or documentation requirements + +#### **Workflow Enforcement** + +The system automatically enforces workflow constraints through the core always-on rules: + +**Before Every Interaction**: +1. **Read current workflow state** from `.cursor/rules/.workflow_state.json` +2. **Identify current mode** and its constraints +3. **Validate user request** against current mode constraints +4. **Enforce constraints** before generating response +5. **Guide model behavior** based on current mode + +**Mode-Specific Enforcement**: +- **Diagnosis Mode**: Blocks modification, creation, building, and commits +- **Fixing Mode**: Allows full implementation and testing capabilities +- **Planning Mode**: Focuses on design and architecture, blocks implementation +- **Research Mode**: Enables investigation and analysis, blocks modification +- **Documentation Mode**: Allows writing and editing, blocks implementation + +#### **Workflow Transitions** + +To change workflow modes, invoke the appropriate meta-rule: + +```bash +# Switch to bug fixing mode +@meta_bug_fixing.mdc + +# Switch to feature planning mode +@meta_feature_planning.mdc + +# Switch to documentation mode +@meta_documentation.mdc +``` + +**Transition Requirements**: +- **Mode Changes**: Require explicit meta-rule invocation +- **State Updates**: Automatically update workflow state file +- **Constraint Enforcement**: Immediately apply new mode constraints +- **History Tracking**: Automatically maintained in `workflowHistory` array +- **Timestamp Recording**: Each transition recorded with UTC timestamp + +#### **Integration with Development Process** + +The workflow system integrates seamlessly with existing development practices: + +**Version Control**: +- All commits must follow TimeSafari commit message standards +- Security audit checklists are enforced regardless of workflow mode +- Documentation updates are required for substantial changes + +**Quality Assurance**: +- Code quality standards (PEP8, TypeScript, etc.) are always enforced +- Testing requirements apply to all implementation work +- Documentation standards are maintained across all phases + +**Build System**: +- Build Architecture Guard protects critical build files +- Platform-specific build processes respect workflow constraints +- Asset generation follows established patterns + +**Migration Context**: +- Database migration work respects investigation vs. implementation phases +- Component migration progress is tracked through workflow states + +## Usage Guidelines + +1. **Always-On Rules**: Start with `meta_core_always_on.mdc` for every + single prompt +2. **Core Rules**: Always apply rules from `core/` directory +3. **Context-Specific**: Use rules from appropriate subdirectories based on + your task +4. **Meta-Rules**: Use workflow-specific meta-rules for specialized tasks + - **Documentation**: Use `meta_documentation.mdc` for all documentation work + - **Getting Started**: See `docs/meta_rule_usage_guide.md` for comprehensive usage instructions +5. **Cross-References**: All files contain updated cross-references to + reflect the new structure +6. **Validation**: All files pass markdown validation and maintain + consistent formatting + +## Benefits of New Organization + +1. **Logical grouping** - Related rules are now co-located +2. **Easier navigation** - Developers can quickly find relevant rules +3. **Better maintainability** - Clear separation of concerns +4. **Scalable structure** - Easy to add new rules in appropriate categories +5. **Consistent cross-references** - All file links updated and working +6. **Workflow bundling** - Meta-rules provide high-level workflow guidance +7. **Feedback integration** - Built-in feedback mechanisms for continuous improvement +8. **Educational focus** - Documentation emphasizes human competence over technical description + +## File Naming Convention + +- **Lowercase with underscores**: `file_name.mdc` +- **Descriptive names**: Names clearly indicate the rule's purpose +- **Consistent extensions**: All files use `.mdc` extension + +## Maintenance + +- **Cross-references**: Update when moving files between directories +- **Markdown validation**: Run `npm run markdown:check` after any changes +- **Organization**: Keep related rules in appropriate subdirectories +- **Documentation**: Update this README when adding new rules or directories + +--- + +**Status**: Active organization structure +**Last Updated**: 2025-08-21 +**Maintainer**: Development team diff --git a/.cursor/rules/always_on_rules.mdc b/.cursor/rules/always_on_rules.mdc new file mode 100644 index 00000000..33128c17 --- /dev/null +++ b/.cursor/rules/always_on_rules.mdc @@ -0,0 +1,192 @@ +# Meta-Rule: Core Always-On Rules + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Core rules for every prompt + +## Purpose + +This meta-rule bundles the core rules that should be applied to **every single +prompt** because they define fundamental behaviors, principles, and context +that are essential for all AI interactions. + +## When to Use + +**ALWAYS** - These rules apply to every single prompt, regardless of the task +or context. They form the foundation for all AI assistant behavior. + +## Bundled Rules + +### **Core Human Competence Principles** + +- **`core/base_context.mdc`** - Human competence first principles, interaction + guidelines, and output contract requirements +- **`core/less_complex.mdc`** - Minimalist solution principle and complexity + guidelines + +### **Time & Context Standards** + +- **`development/time.mdc`** - Time handling principles and UTC standards +- **`development/time_examples.mdc`** - Practical time implementation examples +- **`development/time_implementation.mdc`** - Detailed time implementation + guidelines + +### **Version Control & Process** + +- **`workflow/version_control.mdc`** - Version control principles and commit + guidelines +- **`workflow/commit_messages.mdc`** - Commit message format and conventions + +### **Application Context** + +- **`app/timesafari.mdc`** - Core TimeSafari application context and + development principles +- **`app/timesafari_development.mdc`** - TimeSafari-specific development + workflow and quality standards + +## Why These Rules Are Always-On + +### **Base Context** + +- **Human Competence First**: Every interaction must increase human competence +- **Output Contract**: All responses must follow the required structure +- **Competence Hooks**: Learning and collaboration must be built into every response + +### **Time Standards** + +- **UTC Consistency**: All timestamps must use UTC for system operations +- **Evidence Collection**: Time context is essential for debugging and investigation +- **Cross-Platform**: Time handling affects all platforms and features + +### **Version Control** + +- **Commit Standards**: Every code change must follow commit message conventions +- **Process Consistency**: Version control affects all development work +- **Team Collaboration**: Commit standards enable effective team communication + +### **Application Context** + +- **Platform Awareness**: Every task must consider web/mobile/desktop platforms +- **Architecture Principles**: All work must follow TimeSafari patterns +- **Development Standards**: Quality and testing requirements apply to all work + +## Application Priority + +### **Primary (Apply First)** + +1. **Base Context** - Human competence and output contract +2. **Time Standards** - UTC and timestamp requirements +3. **Application Context** - TimeSafari principles and platforms + +### **Secondary (Apply as Needed)** + +1. **Version Control** - When making code changes +2. **Complexity Guidelines** - When evaluating solution approaches + +## Integration with Other Meta-Rules + +### **Feature Planning** + +- Base context ensures human competence focus +- Time standards inform planning and estimation +- Application context drives platform considerations + +### **Bug Diagnosis** + +- Base context ensures systematic investigation +- Time standards enable proper evidence collection +- Application context provides system understanding + +### **Bug Fixing** + +- Base context ensures quality implementation +- Time standards maintain logging consistency +- Application context guides testing strategy + +### **Feature Implementation** + +- Base context ensures proper development approach +- Time standards maintain system consistency +- Application context drives architecture decisions + +## Success Criteria + +- [ ] **Base context applied** to every single prompt +- [ ] **Time standards followed** for all timestamps and logging +- [ ] **Version control standards** applied to all code changes +- [ ] **Application context considered** for all platform work +- [ ] **Human competence focus** maintained in all interactions +- [ ] **Output contract structure** followed in all responses + +## Common Pitfalls + +- **Don't skip base context** - loses human competence focus +- **Don't ignore time standards** - creates inconsistent timestamps +- **Don't forget application context** - misses platform considerations +- **Don't skip version control** - creates inconsistent commit history +- **Don't lose competence focus** - reduces learning value + +## Feedback & Improvement + +### **Rule Effectiveness Ratings (1-5 scale)** + +- **Base Context**: ___/5 - Comments: _______________ +- **Time Standards**: ___/5 - Comments: _______________ +- **Version Control**: ___/5 - Comments: _______________ +- **Application Context**: ___/5 - Comments: _______________ + +### **Always-On Effectiveness** + +- **Consistency**: Are these rules applied consistently across all prompts? +- **Value**: Do these rules add value to every interaction? +- **Overhead**: Are these rules too burdensome for simple tasks? + +### **Integration Feedback** + +- **With Other Meta-Rules**: How well do these integrate with workflow rules? +- **Context Switching**: Do these rules help or hinder context switching? +- **Learning Curve**: Are these rules easy for new users to understand? + +### **Overall Experience** + +- **Quality Improvement**: Do these rules improve response quality? +- **Efficiency**: Do these rules make interactions more efficient? +- **Recommendation**: Would you recommend keeping these always-on? + +## Model Implementation Checklist + +### Before Every Prompt + +- [ ] **Base Context**: Ensure human competence principles are active +- [ ] **Time Standards**: Verify UTC and timestamp requirements are clear +- [ ] **Application Context**: Confirm TimeSafari context is loaded +- [ ] **Version Control**: Prepare commit standards if code changes are needed + +### During Response Creation + +- [ ] **Output Contract**: Follow required response structure +- [ ] **Competence Hooks**: Include learning and collaboration elements +- [ ] **Time Consistency**: Apply UTC standards for all time references +- [ ] **Platform Awareness**: Consider all target platforms + +### After Response Creation + +- [ ] **Validation**: Verify all always-on rules were applied +- [ ] **Quality Check**: Ensure response meets competence standards +- [ ] **Context Review**: Confirm application context was properly considered +- [ ] **Feedback Collection**: Note any issues with always-on application + +--- + +**See also**: + +- `.cursor/rules/meta_feature_planning.mdc` for workflow-specific rules +- `.cursor/rules/meta_bug_diagnosis.mdc` for investigation workflows +- `.cursor/rules/meta_bug_fixing.mdc` for fix implementation +- `.cursor/rules/meta_feature_implementation.mdc` for feature development + +**Status**: Active core always-on meta-rule +**Priority**: Critical (applies to every prompt) +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: All AI interactions, Development team diff --git a/.cursor/rules/app/architectural_decision_record.mdc b/.cursor/rules/app/architectural_decision_record.mdc new file mode 100644 index 00000000..c5622722 --- /dev/null +++ b/.cursor/rules/app/architectural_decision_record.mdc @@ -0,0 +1,193 @@ +# TimeSafari Cross-Platform Architecture Guide + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Architecture guidelines + +## 1. Platform Support Matrix + +| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | +|---------|-----------|--------------------|-------------------| +| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | + Not Implemented | +| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | +| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | +| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | +| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env + checks | + +## 2. Project Structure + +### Core Directories + +``` + +src/ +├── components/ # Vue components +├── services/ # Platform services and business logic +├── views/ # Page components +├── router/ # Vue router configuration +├── types/ # TypeScript type definitions +├── utils/ # Utility functions +├── lib/ # Core libraries +├── platforms/ # Platform-specific implementations +├── electron/ # Electron-specific code +├── constants/ # Application constants +├── db/ # Database related code +├── interfaces/ # TypeScript interfaces +└── assets/ # Static assets + +``` + +### Entry Points + +- `main.ts` → Base entry + +- `main.common.ts` → Shared init + +- `main.capacitor.ts` → Mobile entry + +- `main.electron.ts` → Electron entry + +- `main.web.ts` → Web entry + +## 3. Service Architecture + +### Service Organization + +```tree + +services/ +├── QRScanner/ +│ ├── WebInlineQRScanner.ts +│ └── interfaces.ts +├── platforms/ +│ ├── WebPlatformService.ts +│ ├── CapacitorPlatformService.ts +│ └── ElectronPlatformService.ts +└── factory/ + └── PlatformServiceFactory.ts + +``` + +### Factory Pattern + +Use a **singleton factory** to select platform services via +`process.env.VITE_PLATFORM`. + +## 4. Feature Guidelines + +### QR Code Scanning + +- Define `QRScannerService` interface. + +- Implement platform-specific classes (`WebInlineQRScanner`, Capacitor, + + etc). + +- Provide `addListener` and `onStream` hooks for composability. + +### Deep Linking + +- URL format: `timesafari://[/][?query=value]` + +- Web: `router.beforeEach` → parse query + +- Capacitor: `App.addListener("appUrlOpen", …)` + +## 5. Build Process + +- `vite.config.common.mts` → shared config + +- Platform configs: `vite.config.web.mts`, `.capacitor.mts`, + + `.electron.mts` + +- Use `process.env.VITE_PLATFORM` for conditional loading. + +```bash + +npm run build:web +npm run build:capacitor +npm run build:electron + +``` + +## 6. Testing Strategy + +- **Unit Tests**: Jest for business logic and utilities + +- **E2E Tests**: Playwright for critical user journeys + +- **Platform Tests**: Test platform-specific implementations + +- **Integration Tests**: Test service interactions + +## 7. Key Principles + +### Platform Independence + +- **Abstract platform differences** behind interfaces + +- **Use factory pattern** for service selection + +- **Maintain consistent APIs** across platforms + +- **Graceful degradation** when features unavailable + +### Code Organization + +- **Single responsibility** for each service + +- **Interface segregation** for platform services + +- **Dependency injection** via mixins + +- **Composition over inheritance** + +--- + +**See also**: + +- `.cursor/rules/app/architectural_implementation.mdc` for + + detailed implementation details + +- `.cursor/rules/app/architectural_patterns.mdc` for architectural patterns and + + examples + +**Status**: Active architecture guidelines +**Priority**: Critical +**Estimated Effort**: Ongoing reference +**Dependencies**: timesafari.mdc +**Stakeholders**: Development team, Architecture team + +- [ ] Have relevant ADRs been updated/linked? + +- [ ] Did I add competence hooks or prompts for the team? + +- [ ] Was human interaction (sync/review/demo) scheduled? + +## Model Implementation Checklist + +### Before Architectural Decisions + +- [ ] **Decision Context**: Understand the architectural challenge to be addressed +- [ ] **Stakeholder Identification**: Identify all decision makers and affected parties +- [ ] **Research**: Research alternatives and gather evidence +- [ ] **Impact Assessment**: Assess impact on existing architecture + +### During Architectural Decisions + +- [ ] **Context Documentation**: Document the context and forces at play +- [ ] **Decision Recording**: Record the decision and rationale clearly +- [ ] **Consequences Analysis**: Analyze positive, negative, and neutral consequences +- [ ] **Alternatives Documentation**: Document alternatives considered and why rejected + +### After Architectural Decisions + +- [ ] **ADR Creation**: Create or update Architectural Decision Record +- [ ] **Team Communication**: Communicate decision to all stakeholders +- [ ] **Implementation Planning**: Plan implementation of the architectural decision +- [ ] **Documentation Update**: Update relevant architectural documentation diff --git a/.cursor/rules/app/architectural_examples.mdc b/.cursor/rules/app/architectural_examples.mdc new file mode 100644 index 00000000..babc8e1a --- /dev/null +++ b/.cursor/rules/app/architectural_examples.mdc @@ -0,0 +1,246 @@ +# Time Safari Architecture — Examples and Testing + +> **Agent role**: Reference this file for architectural examples and + testing patterns when working with TimeSafari architecture. + +## Error Handling Patterns + +### Global Error Handler + +```typescript + +// main.ts +app.config.errorHandler = (err, instance, info) => { + const componentName = instance?.$options?.name || 'Unknown'; + logger.error(`[${componentName}] Vue error`, err, info); +}; + +window.addEventListener('unhandledrejection', (event) => { + logger.error('[Global] Unhandled promise rejection', event.reason); +}); + +``` + +### Platform-Specific Error Wrapping + +```typescript + +// services/platforms/CapacitorPlatformService.ts +export class CapacitorPlatformService { + async getFileContents(path: string): Promise { + try { + const result = await Filesystem.readFile({ + path: path, + encoding: 'utf8' + }); + return result.data; + } catch (error) { + logger.error('[Capacitor API Error] Failed to read file', error, path); + throw new Error(`Failed to read file: ${path}`); + } + } +} + +``` + +## Testing Patterns + +### Platform-Specific Test Skipping + +```typescript + +// tests/QRScanner.test.ts +describe('QRScanner Service', () => { + test('should start scanning on web', async () => { + test.skip(process.env.VITE_PLATFORM !== 'web', 'Web-only test'); + + const scanner = new WebInlineQRScanner(); + await scanner.startScanning(); + // Assert scanning started + }); + + test('should start scanning on mobile', async () => { + test.skip(process.env.VITE_PLATFORM !== 'capacitor', 'Mobile-only test'); + + const scanner = new CapacitorQRScanner(); + await scanner.startScanning(); + // Assert scanning started + }); +}); + +``` + +### Mock Service Testing + +```typescript + +// tests/mocks/QRScannerMock.ts +export class QRScannerMock implements QRScannerService { + private isScanning = false; + private listeners: Map = new Map(); + + async startScanning(): Promise { + this.isScanning = true; + this.emit('scanningStarted'); + } + + async stopScanning(): Promise { + this.isScanning = false; + this.emit('scanningStopped'); + } + + addListener(event: string, callback: Function): void { + if (!this.listeners.has(event)) { + this.listeners.set(event, []); + } + this.listeners.get(event)!.push(callback); + } + + removeListener(event: string, callback: Function): void { + const callbacks = this.listeners.get(event); + if (callbacks) { + const index = callbacks.indexOf(callback); + if (index > -1) { + callbacks.splice(index, 1); + } + } + } + + private emit(event: string, ...args: any[]): void { + const callbacks = this.listeners.get(event); + if (callbacks) { + callbacks.forEach(callback => callback(...args)); + } + } + + getScanningState(): boolean { + return this.isScanning; + } +} + +``` + +## Integration Examples + +### Service Composition + +```typescript + +// services/QRScannerService.ts +export class QRScannerService { + constructor( + private platformService: PlatformService, + private notificationService: NotificationService + ) {} + + async startScanning(): Promise { + try { + await this.platformService.startCamera(); + this.notificationService.show('Camera started'); + } catch (error) { + this.notificationService.showError('Failed to start camera'); + throw error; + } + } +} + +``` + +### Component Integration + +```typescript + +// components/QRScannerDialog.vue +export default class QRScannerDialog extends Vue { + @Inject() private qrScannerService!: QRScannerService; + + async mounted() { + try { + await this.qrScannerService.startScanning(); + } catch (error) { + this.$notify.error('Failed to start scanner'); + } + } + + beforeDestroy() { + this.qrScannerService.stopScanning(); + } +} + +``` + +## Best Practices + +### Service Design + +- Keep services focused and single-purpose + +- Use dependency injection for service composition + +- Implement proper error handling and logging + +- Provide clear interfaces and contracts + +### Testing Strategy + +- Test platform-specific behavior separately + +- Use mocks for external dependencies + +- Test error conditions and edge cases + +- Validate service contracts and interfaces + +### Error Handling + +- Log errors with appropriate context + +- Provide user-friendly error messages + +- Implement graceful degradation + +- Handle platform-specific error scenarios + +--- + +**See also**: + +- `.cursor/rules/app/architectural_decision_record.mdc` for + + core architecture principles + +- `.cursor/rules/app/architectural_implementation.mdc` for + + implementation details + +- `.cursor/rules/app/architectural_patterns.mdc` for core patterns + +**Status**: Active examples and testing guide +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: architectural_patterns.mdc +**Stakeholders**: Development team, Testing team + +## Model Implementation Checklist + +### Before Architectural Examples + +- [ ] **Pattern Selection**: Choose appropriate architectural pattern for the use + case +- [ ] **Service Design**: Plan service structure and dependencies +- [ ] **Testing Strategy**: Plan testing approach for the example +- [ ] **Error Handling**: Plan error handling and logging strategy + +### During Architectural Examples + +- [ ] **Service Implementation**: Implement focused, single-purpose services +- [ ] **Dependency Injection**: Use proper dependency injection patterns +- [ ] **Error Handling**: Implement proper error handling and logging +- [ ] **Interface Design**: Provide clear interfaces and contracts + +### After Architectural Examples + +- [ ] **Testing Execution**: Test platform-specific behavior separately +- [ ] **Service Validation**: Validate service contracts and interfaces +- [ ] **Error Testing**: Test error conditions and edge cases +- [ ] **Documentation**: Update architectural examples documentation diff --git a/.cursor/rules/app/architectural_implementation.mdc b/.cursor/rules/app/architectural_implementation.mdc new file mode 100644 index 00000000..bd853860 --- /dev/null +++ b/.cursor/rules/app/architectural_implementation.mdc @@ -0,0 +1,139 @@ +# Time Safari Architecture — Implementation Details + +> **Agent role**: Reference this file for detailed implementation details when + working with TimeSafari architecture implementation. + +## Error Handling + +- Global Vue error handler → logs with component name. + +- Platform-specific wrappers log API errors with platform prefix + + (`[Capacitor API Error]`, etc). + +- Use structured logging (not `console.log`). + +## Best Practices + +- Keep platform code **isolated** in `platforms/`. + +- Always define a **shared interface** first. + +- Use feature detection, not platform detection, when possible. + +- Dependency injection for services → improves testability. + +- Maintain **Competence Hooks** in PRs (2–3 prompts for dev + + discussion). + +## Dependency Management + +- Key deps: `@capacitor/core`, `electron`, `vue`. + +- Use conditional `import()` for platform-specific libs. + +## Security Considerations + +- **Permissions**: Always check + request gracefully. + +- **Storage**: Secure storage for sensitive data; encrypt when possible. + +- **Audits**: Schedule quarterly security reviews. + +## ADR Process + +- All major architecture choices → log in `doc/adr/`. + +- Use ADR template with Context, Decision, Consequences, Status. + +- Link related ADRs in PR descriptions. + +> 🔗 **Human Hook:** When proposing a new ADR, schedule a 30-min +> design sync for discussion, not just async review. + +## Collaboration Hooks + +- **QR features**: Sync with Security before merging → permissions & + + privacy. + +- **New platform builds**: Demo in team meeting → confirm UX + + differences. + +- **Critical ADRs**: Present in guild or architecture review. + +## Testing Implementation + +- **Unit tests** for services. + +- **Playwright** for Web + Capacitor: + + - `playwright.config-local.ts` includes web + Pixel 5. + +- **Electron tests**: add `spectron` or Playwright-Electron. + +- Mark tests with platform tags: + + ```ts + + test.skip(!process.env.MOBILE_TEST, "Mobile-only test"); + + ``` + +> 🔗 **Human Hook:** Before merging new tests, hold a short sync (≤15 +> min) with QA to align on coverage and flaky test risks. + +## Self-Check + +- [ ] Does this feature implement a shared interface? + +- [ ] Are fallbacks + errors handled gracefully? + +- [ ] Have relevant ADRs been updated/linked? + +- [ ] Did I add competence hooks or prompts for the team? + +- [ ] Was human interaction (sync/review/demo) scheduled? + +--- + +**See also**: + +- `.cursor/rules/app/architectural_decision_record.mdc` for + + core architecture principles + +- `.cursor/rules/app/architectural_patterns.mdc` for architectural patterns and + + examples + +**Status**: Active implementation guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: architectural_decision_record.mdc +**Stakeholders**: Development team, Architecture team + +## Model Implementation Checklist + +### Before Architectural Implementation + +- [ ] **Interface Review**: Verify feature implements shared interface +- [ ] **ADR Review**: Check if ADR is required for major changes +- [ ] **Security Assessment**: Assess security implications for QR features +- [ ] **Platform Planning**: Plan platform-specific implementation details + +### During Architectural Implementation + +- [ ] **Interface Implementation**: Implement shared interfaces consistently +- [ ] **Error Handling**: Implement graceful fallbacks and error handling +- [ ] **Testing Strategy**: Plan unit tests for services and E2E tests +- [ ] **Human Interaction**: Schedule syncs/reviews/demos as needed + +### After Architectural Implementation + +- [ ] **Interface Validation**: Verify shared interfaces are properly implemented +- [ ] **Testing Execution**: Run unit tests and platform-specific tests +- [ ] **ADR Updates**: Update relevant ADRs and link in PR descriptions +- [ ] **Team Communication**: Share implementation results with team diff --git a/.cursor/rules/app/architectural_patterns.mdc b/.cursor/rules/app/architectural_patterns.mdc new file mode 100644 index 00000000..026a8354 --- /dev/null +++ b/.cursor/rules/app/architectural_patterns.mdc @@ -0,0 +1,214 @@ +# Time Safari Architecture — Patterns and Examples + +> **Agent role**: Reference this file for architectural patterns and +> examples when working with TimeSafari architecture design. + +## Architectural Patterns + +### Factory Pattern Implementation + +```typescript +// PlatformServiceFactory.ts +export class PlatformServiceFactory { + private static instance: PlatformServiceFactory; + + static getInstance(): PlatformServiceFactory { + if (!PlatformServiceFactory.instance) { + PlatformServiceFactory.instance = new PlatformServiceFactory(); + } + return PlatformServiceFactory.instance; + } + + getQRScannerService(): QRScannerService { + const platform = process.env.VITE_PLATFORM; + + switch (platform) { + case 'web': + return new WebInlineQRScanner(); + case 'capacitor': + return new CapacitorQRScanner(); + case 'electron': + return new ElectronQRScanner(); + default: + throw new Error(`Unsupported platform: ${platform}`); + } + } +} +``` + +### Service Interface Definition + +```typescript +// interfaces/QRScannerService.ts +export interface QRScannerService { + startScanning(): Promise; + stopScanning(): Promise; + addListener(event: string, callback: Function): void; + removeListener(event: string, callback: Function): void; +} +``` + +### Platform-Specific Implementation + +```typescript +// services/QRScanner/WebInlineQRScanner.ts +export class WebInlineQRScanner implements QRScannerService { + private listeners: Map = new Map(); + + async startScanning(): Promise { + // Web-specific implementation + const stream = await navigator.mediaDevices.getUserMedia({ video: true }); + // Process video stream for QR codes + } + + async stopScanning(): Promise { + // Stop video stream + } + + addListener(event: string, callback: Function): void { + if (!this.listeners.has(event)) { + this.listeners.set(event, []); + } + this.listeners.get(event)!.push(callback); + } + + removeListener(event: string, callback: Function): void { + const callbacks = this.listeners.get(event); + if (callbacks) { + const index = callbacks.indexOf(callback); + if (index > -1) { + callbacks.splice(index, 1); + } + } + } +} +``` + +## Deep Linking Implementation + +### URL Format + +``` +timesafari://[/][?query=value] +``` + +### Web Implementation + +```typescript +// router/index.ts +router.beforeEach((to, from, next) => { + // Parse deep link parameters + if (to.query.deepLink) { + const deepLink = to.query.deepLink as string; + // Process deep link + handleDeepLink(deepLink); + } + next(); +}); + +function handleDeepLink(deepLink: string) { + // Parse and route deep link + const url = new URL(deepLink); + const route = url.pathname; + const params = url.searchParams; + + // Navigate to appropriate route + router.push({ name: route, query: Object.fromEntries(params) }); +} +``` + +### Capacitor Implementation + +```typescript +// main.capacitor.ts +import { App } from '@capacitor/app'; + +App.addListener('appUrlOpen', (data) => { + const url = data.url; + // Parse deep link and navigate + handleDeepLink(url); +}); +``` + +## Platform Detection + +### Feature Detection vs Platform Detection + +```typescript +// ✅ Good: Feature detection +function hasCameraAccess(): boolean { + return 'mediaDevices' in navigator && + 'getUserMedia' in navigator.mediaDevices; +} + +// ❌ Bad: Platform detection +function isWeb(): boolean { + return process.env.VITE_PLATFORM === 'web'; +} +``` + +### Conditional Imports + +```typescript +// services/platforms/index.ts +export async function getPlatformService() { + const platform = process.env.VITE_PLATFORM; + + switch (platform) { + case 'capacitor': + const { CapacitorPlatformService } = + await import('./CapacitorPlatformService'); + return new CapacitorPlatformService(); + case 'electron': + const { ElectronPlatformService } = + await import('./ElectronPlatformService'); + return new ElectronPlatformService(); + default: + const { WebPlatformService } = + await import('./WebPlatformService'); + return new WebPlatformService(); + } +} +``` + +--- + +**See also**: + +- `.cursor/rules/app/architectural_decision_record.mdc` for core + architecture principles +- `.cursor/rules/app/architectural_implementation.mdc` for + implementation details +- `.cursor/rules/app/architectural_examples.mdc` for examples and + testing patterns + +**Status**: Active patterns and examples +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: architectural_decision_record.mdc, + architectural_implementation.mdc +**Stakeholders**: Development team, Architecture team + +## Model Implementation Checklist + +### Before Architectural Patterns + +- [ ] **Pattern Selection**: Choose appropriate architectural pattern for the use + case +- [ ] **Platform Analysis**: Identify platform-specific requirements +- [ ] **Service Planning**: Plan service structure and dependencies +- [ ] **Testing Strategy**: Plan testing approach for the pattern + +### During Architectural Patterns + +- [ ] **Pattern Implementation**: Implement chosen architectural pattern +- [ ] **Platform Abstraction**: Use platform abstraction layers appropriately +- [ ] **Service Composition**: Compose services using dependency injection +- [ ] **Interface Design**: Provide clear interfaces and contracts + +### After Architectural Patterns + +- [ ] **Pattern Validation**: Verify pattern is implemented correctly +- [ ] **Platform Testing**: Test across all target platforms +- [ ] **Service Testing**: Test service composition and dependencies +- [ ] **Documentation**: Update architectural patterns documentation diff --git a/.cursor/rules/app/timesafari.mdc b/.cursor/rules/app/timesafari.mdc new file mode 100644 index 00000000..ad8a2249 --- /dev/null +++ b/.cursor/rules/app/timesafari.mdc @@ -0,0 +1,173 @@ +--- +alwaysApply: false +--- +# Time Safari Context + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Core application context + +## Project Overview + +Time Safari is an application designed to foster community building through +gifts, gratitude, and collaborative projects. The app makes it easy and +intuitive for users of any age and capability to recognize contributions, +build trust networks, and organize collective action. It is built on services +that preserve privacy and data sovereignty. + +## Core Goals + +1. **Connect**: Make it easy, rewarding, and non-threatening for people to + + connect with others who have similar interests, and to initiate activities + together. + +2. **Reveal**: Widely advertise the great support and rewards that are being + + given and accepted freely, especially non-monetary ones, showing the impact + gifts make in people's lives. + +## Technical Foundation + +### Architecture + +- **Privacy-preserving claims architecture** via endorser.ch + +- **Decentralized Identifiers (DIDs)**: User identities based on + + public/private key pairs stored on devices + +- **Cryptographic Verification**: All claims and confirmations are + + cryptographically signed + +- **User-Controlled Visibility**: Users explicitly control who can see their + + identifiers and data + +- **Cross-Platform**: Web (PWA), Mobile (Capacitor), Desktop (Electron) + +### Current Database State + +- **Database**: SQLite via Absurd SQL (browser) and native SQLite + + (mobile/desktop) + +- **Legacy Support**: IndexedDB (Dexie) for backward compatibility + +- **Status**: Modern database architecture fully implemented + +### Core Technologies + +- **Frontend**: Vue 3 + TypeScript + vue-facing-decorator + +- **Styling**: TailwindCSS + +- **Build**: Vite with platform-specific configs + +- **Testing**: Playwright E2E, Jest unit tests + +- **Database**: SQLite (Absurd SQL in browser), IndexedDB (legacy) + +- **State**: Pinia stores + +- **Platform Services**: Abstracted behind interfaces with factory pattern + +## Development Principles + +### Code Organization + +- **Platform Services**: Abstract platform-specific code behind interfaces + +- **Service Factory**: Use `PlatformServiceFactory` for platform selection + +- **Type Safety**: Strict TypeScript, no `any` types, use type guards + +- **Modern Architecture**: Use current platform service patterns + +### Architecture Patterns + +- **Dependency Injection**: Services injected via mixins and factory pattern + +- **Interface Segregation**: Small, focused interfaces over large ones + +- **Composition over Inheritance**: Prefer mixins and composition + +- **Single Responsibility**: Each component/service has one clear purpose + +### Testing Strategy + +- **E2E**: Playwright for critical user journeys + +- **Unit**: Jest with F.I.R.S.T. principles + +- **Platform Coverage**: Web + Capacitor (Pixel 5) in CI + +- **Quality Assurance**: Comprehensive testing and validation + +## Current Development Focus + +### Active Development + +- **Feature Development**: Build new functionality using modern platform + + services + +- **Performance Optimization**: Improve app performance and user experience + +- **Platform Enhancement**: Leverage platform-specific capabilities + +- **Code Quality**: Maintain high standards and best practices + +### Development Metrics + +- **Code Quality**: High standards maintained across all platforms + +- **Performance**: Optimized for all target devices + +- **Testing**: Comprehensive coverage maintained + +- **User Experience**: Focus on intuitive, accessible interfaces + +--- + +**See also**: + +- `.cursor/rules/app/timesafari_platforms.mdc` for platform-specific details + +- `.cursor/rules/app/timesafari_development.mdc` for + + development workflow details + +**Status**: Active application context +**Priority**: Critical +**Estimated Effort**: Ongoing reference +**Dependencies**: None +**Stakeholders**: Development team, Product team + +- **Dependencies**: Vue 3, TypeScript, SQLite, Capacitor, Electron + +- **Stakeholders**: Development team, Product team + +## Model Implementation Checklist + +### Before TimeSafari Development + +- [ ] **Application Context**: Understand TimeSafari's community-building purpose +- [ ] **Platform Analysis**: Identify target platforms (web, mobile, desktop) +- [ ] **Architecture Review**: Review current platform service patterns +- [ ] **Testing Strategy**: Plan testing approach for all platforms + +### During TimeSafari Development + +- [ ] **Platform Services**: Use abstracted platform services via interfaces +- [ ] **Type Safety**: Implement strict TypeScript with type guards +- **Modern Architecture**: Follow current platform service patterns +- [ ] **Performance Focus**: Ensure performance on all target devices + +### After TimeSafari Development + +- [ ] **Cross-Platform Testing**: Test functionality across all platforms +- [ ] **Performance Validation**: Verify performance meets requirements +- [ ] **Code Quality**: Ensure high standards maintained +- [ ] **Documentation Update**: Update relevant documentation diff --git a/.cursor/rules/app/timesafari_development.mdc b/.cursor/rules/app/timesafari_development.mdc new file mode 100644 index 00000000..e72c68dc --- /dev/null +++ b/.cursor/rules/app/timesafari_development.mdc @@ -0,0 +1,174 @@ +# Time Safari Development — Workflow and Processes + +> **Agent role**: Reference this file for development workflow details when + working with TimeSafari development processes. + +## Development Workflow + +### Build Commands + +```bash + +# Web (development) + +npm run build:web + +# Mobile + +npm run build:capacitor +npm run build:native + +# Desktop + +npm run build:electron +npm run build:electron:appimage +npm run build:electron:deb +npm run build:electron:dmg + +``` + +### Testing Commands + +```bash + +# Web E2E + +npm run test:web + +# Mobile + +npm run test:mobile +npm run test:android +npm run test:ios + +# Type checking + +npm run type-check +npm run lint-fix + +``` + +## Development Principles + +### Code Organization + +- **Platform Services**: Abstract platform-specific code behind interfaces + +- **Service Factory**: Use `PlatformServiceFactory` for platform selection + +- **Type Safety**: Strict TypeScript, no `any` types, use type guards + +- **Modern Architecture**: Use current platform service patterns + +### Architecture Patterns + +- **Dependency Injection**: Services injected via mixins and factory pattern + +- **Interface Segregation**: Small, focused interfaces over large ones + +- **Composition over Inheritance**: Prefer mixins and composition + +- **Single Responsibility**: Each component/service has one clear purpose + +### Testing Strategy + +- **E2E**: Playwright for critical user journeys + +- **Unit**: Jest with F.I.R.S.T. principles + +- **Platform Coverage**: Web + Capacitor (Pixel 5) in CI + +- **Quality Assurance**: Comprehensive testing and validation + +## Current Development Focus + +### Active Development + +- **Feature Development**: Build new functionality using modern platform + + services + +- **Performance Optimization**: Improve app performance and user experience + +- **Platform Enhancement**: Leverage platform-specific capabilities + +- **Code Quality**: Maintain high standards and best practices + +### Development Metrics + +- **Code Quality**: High standards maintained across all platforms + +- **Performance**: Optimized for all target devices + +- **Testing**: Comprehensive coverage maintained + +- **User Experience**: Focus on intuitive, accessible interfaces + +## Development Environment + +### Required Tools + +- **Node.js**: LTS version with npm + +- **Git**: Version control with proper branching strategy + +- **IDE**: VS Code with recommended extensions + +- **Platform Tools**: Android Studio, Xcode (for mobile development) + +### Environment Setup + +1. **Clone Repository**: `git clone ` + +2. **Install Dependencies**: `npm install` + +3. **Environment Variables**: Copy `.env.example` to `.env.local` + +4. **Platform Setup**: Follow platform-specific setup guides + +### Quality Assurance + +- **Linting**: ESLint with TypeScript rules + +- **Formatting**: Prettier for consistent code style + +- **Type Checking**: TypeScript strict mode enabled + +- **Testing**: Comprehensive test coverage requirements + +--- + +**See also**: + +- `.cursor/rules/app/timesafari.mdc` for core application context + +- `.cursor/rules/app/timesafari_platforms.mdc` for platform-specific details + +**Status**: Active development workflow +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: timesafari.mdc, timesafari_platforms.mdc +**Stakeholders**: Development team, DevOps team + +## Model Implementation Checklist + +### Before TimeSafari Development + +- [ ] **Environment Setup**: Verify development environment is ready +- [ ] **Platform Tools**: Ensure platform-specific tools are available +- [ ] **Dependencies**: Check all required dependencies are installed +- [ ] **Environment Variables**: Configure local environment variables + +### During TimeSafari Development + +- [ ] **Platform Services**: Use modern platform service patterns +- [ ] **Code Quality**: Follow ESLint and TypeScript strict rules +- [ ] **Testing**: Implement comprehensive testing strategy +- [ ] **Performance**: Optimize for all target platforms + +### After TimeSafari Development + +- [ ] **Quality Checks**: Run linting, formatting, and type checking +- [ ] **Testing**: Execute comprehensive tests across platforms +- [ ] **Performance Validation**: Verify performance meets requirements +- [ ] **Documentation**: Update development documentation diff --git a/.cursor/rules/app/timesafari_platforms.mdc b/.cursor/rules/app/timesafari_platforms.mdc new file mode 100644 index 00000000..158e61f2 --- /dev/null +++ b/.cursor/rules/app/timesafari_platforms.mdc @@ -0,0 +1,167 @@ +# Time Safari Platforms — Platform-Specific Considerations + +> **Agent role**: Reference this file for platform-specific details when working + with TimeSafari development across different platforms. + +## Platform-Specific Considerations + +### Web (PWA) + +- **QR Scanning**: WebInlineQRScanner + +- **Deep Linking**: URL parameters + +- **File System**: Limited browser APIs + +- **Build**: `npm run build:web` (development build) + +### Mobile (Capacitor) + +- **QR Scanning**: @capacitor-mlkit/barcode-scanning + +- **Deep Linking**: App URL open events + +- **File System**: Capacitor Filesystem + +- **Build**: `npm run build:capacitor` + +### Desktop (Electron) + +- **File System**: Node.js fs + +- **Build**: `npm run build:electron` + +- **Distribution**: AppImage, DEB, DMG packages + +## Platform Compatibility Requirements + +### Cross-Platform Features + +- **Core functionality** must work identically across all platforms + +- **Platform-specific enhancements** should be additive, not required + +- **Fallback behavior** must be graceful when platform features unavailable + +### Platform-Specific Capabilities + +- **Web**: Browser APIs, PWA features, responsive design + +- **Mobile**: Native device features, offline capability, app store compliance + +- **Desktop**: File system access, system integration, native performance + +## Build and Distribution + +### Build Commands + +```bash + +# Web (development) + +npm run build:web + +# Mobile + +npm run build:capacitor +npm run build:native + +# Desktop + +npm run build:electron +npm run build:electron:appimage +npm run build:electron:deb +npm run build:electron:dmg + +``` + +### Testing Commands + +```bash + +# Web E2E + +npm run test:web + +# Mobile + +npm run test:mobile +npm run test:android +npm run test:ios + +# Type checking + +npm run type-check +npm run lint-fix + +``` + +## Key Constraints + +1. **Privacy First**: User identifiers remain private except when explicitly + + shared + +2. **Platform Compatibility**: Features must work across all target platforms + +3. **Performance**: Must remain performant on older/simpler devices + +4. **Modern Architecture**: New features should use current platform services + +5. **Offline Capability**: Key functionality should work offline when feasible + +## Use Cases to Support + +1. **Community Building**: Tools for finding others with shared interests + +2. **Project Coordination**: Easy proposal and collaboration on projects + +3. **Reputation Building**: Showcasing contributions and reliability + +4. **Governance**: Facilitating decision-making and collective governance + +## Resources + +- **Testing**: `docs/migration-testing/` + +- **Architecture**: `docs/architecture-decisions.md` + +- **Build Context**: `docs/build-modernization-context.md` + +--- + +**See also**: + +- `.cursor/rules/app/timesafari.mdc` for core application context +- `.cursor/rules/app/timesafari_development.mdc` for + + development workflow details + +**Status**: Active platform guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: timesafari.mdc +**Stakeholders**: Development team, Platform teams + +## Model Implementation Checklist + +### Before Platform Development + +- [ ] **Platform Analysis**: Identify all target platforms (web, mobile, desktop) +- [ ] **Feature Requirements**: Understand feature requirements across platforms +- [ ] **Platform Constraints**: Review platform-specific limitations and capabilities +- [ ] **Testing Strategy**: Plan testing approach for all target platforms + +### During Platform Development + +- [ ] **Cross-Platform Implementation**: Implement features across all platforms +- [ ] **Platform Services**: Use current platform services for new features +- [ ] **Performance Optimization**: Ensure performance on older/simpler devices +- [ ] **Offline Capability**: Implement offline functionality where feasible + +### After Platform Development + +- [ ] **Cross-Platform Testing**: Test functionality across all target platforms +- [ ] **Performance Validation**: Verify performance meets requirements +- [ ] **Documentation Update**: Update platform-specific documentation +- [ ] **Team Communication**: Share platform implementation results with team diff --git a/.cursor/rules/architectural_decision_record.mdc b/.cursor/rules/architectural_decision_record.mdc deleted file mode 100644 index fdc53c60..00000000 --- a/.cursor/rules/architectural_decision_record.mdc +++ /dev/null @@ -1,287 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- -# TimeSafari Cross-Platform Architecture Guide - -## 1. Platform Support Matrix - -| Feature | Web (PWA) | Capacitor (Mobile) | Electron (Desktop) | -|---------|-----------|-------------------|-------------------| -| QR Code Scanning | WebInlineQRScanner | @capacitor-mlkit/barcode-scanning | Not Implemented | -| Deep Linking | URL Parameters | App URL Open Events | Not Implemented | -| File System | Limited (Browser API) | Capacitor Filesystem | Electron fs | -| Camera Access | MediaDevices API | Capacitor Camera | Not Implemented | -| Platform Detection | Web APIs | Capacitor.isNativePlatform() | process.env checks | - -## 2. Project Structure - -### 2.1 Core Directories -``` -src/ -├── components/ # Vue components -├── services/ # Platform services and business logic -├── views/ # Page components -├── router/ # Vue router configuration -├── types/ # TypeScript type definitions -├── utils/ # Utility functions -├── lib/ # Core libraries -├── platforms/ # Platform-specific implementations -├── electron/ # Electron-specific code -├── constants/ # Application constants -├── db/ # Database related code -├── interfaces/ # TypeScript interfaces and type definitions -└── assets/ # Static assets -``` - -### 2.2 Entry Points -``` -src/ -├── main.ts # Base entry -├── main.common.ts # Shared initialization -├── main.capacitor.ts # Mobile entry -├── main.electron.ts # Electron entry -└── main.web.ts # Web/PWA entry -``` - -### 2.3 Build Configurations -``` -root/ -├── vite.config.common.mts # Shared config -├── vite.config.capacitor.mts # Mobile build -├── vite.config.electron.mts # Electron build -└── vite.config.web.mts # Web/PWA build -``` - -## 3. Service Architecture - -### 3.1 Service Organization -``` -services/ -├── QRScanner/ # QR code scanning service -│ ├── WebInlineQRScanner.ts -│ └── interfaces.ts -├── platforms/ # Platform-specific services -│ ├── WebPlatformService.ts -│ ├── CapacitorPlatformService.ts -│ └── ElectronPlatformService.ts -└── factory/ # Service factories - └── PlatformServiceFactory.ts -``` - -### 3.2 Service Factory Pattern -```typescript -// PlatformServiceFactory.ts -export class PlatformServiceFactory { - private static instance: PlatformService | null = null; - - public static getInstance(): PlatformService { - if (!PlatformServiceFactory.instance) { - const platform = process.env.VITE_PLATFORM || "web"; - PlatformServiceFactory.instance = createPlatformService(platform); - } - return PlatformServiceFactory.instance; - } -} -``` - -## 4. Feature Implementation Guidelines - -### 4.1 QR Code Scanning - -1. **Service Interface** -```typescript -interface QRScannerService { - checkPermissions(): Promise; - requestPermissions(): Promise; - isSupported(): Promise; - startScan(): Promise; - stopScan(): Promise; - addListener(listener: ScanListener): void; - onStream(callback: (stream: MediaStream | null) => void): void; - cleanup(): Promise; -} -``` - -2. **Platform-Specific Implementation** -```typescript -// WebInlineQRScanner.ts -export class WebInlineQRScanner implements QRScannerService { - private scanListener: ScanListener | null = null; - private isScanning = false; - private stream: MediaStream | null = null; - private events = new EventEmitter(); - - // Implementation of interface methods -} -``` - -### 4.2 Deep Linking - -1. **URL Structure** -```typescript -// Format: timesafari://[/][?queryParam1=value1] -interface DeepLinkParams { - route: string; - params?: Record; - query?: Record; -} -``` - -2. **Platform Handlers** -```typescript -// Capacitor -App.addListener("appUrlOpen", handleDeepLink); - -// Web -router.beforeEach((to, from, next) => { - handleWebDeepLink(to.query); -}); -``` - -## 5. Build Process - -### 5.1 Environment Configuration -```typescript -// vite.config.common.mts -export function createBuildConfig(mode: string) { - return { - define: { - 'process.env.VITE_PLATFORM': JSON.stringify(mode), - // PWA is automatically enabled for web platforms via build configuration - __IS_MOBILE__: JSON.stringify(isCapacitor), - __USE_QR_READER__: JSON.stringify(!isCapacitor) - } - }; -} -``` - -### 5.2 Platform-Specific Builds - -```bash -# Build commands from package.json -"build:web": "vite build --config vite.config.web.mts", -"build:capacitor": "vite build --config vite.config.capacitor.mts", -"build:electron": "vite build --config vite.config.electron.mts" -``` - -## 6. Testing Strategy - -### 6.1 Test Configuration -```typescript -// playwright.config-local.ts -const config: PlaywrightTestConfig = { - projects: [ - { - name: 'web', - use: { browserName: 'chromium' } - }, - { - name: 'mobile', - use: { ...devices['Pixel 5'] } - } - ] -}; -``` - -### 6.2 Platform-Specific Tests -```typescript -test('QR scanning works on mobile', async ({ page }) => { - test.skip(!process.env.MOBILE_TEST, 'Mobile-only test'); - // Test implementation -}); -``` - -## 7. Error Handling - -### 7.1 Global Error Handler -```typescript -function setupGlobalErrorHandler(app: VueApp) { - app.config.errorHandler = (err, instance, info) => { - logger.error("[App Error]", { - error: err, - info, - component: instance?.$options.name - }); - }; -} -``` - -### 7.2 Platform-Specific Error Handling -```typescript -// API error handling for Capacitor -if (process.env.VITE_PLATFORM === 'capacitor') { - logger.error(`[Capacitor API Error] ${endpoint}:`, { - message: error.message, - status: error.response?.status - }); -} -``` - -## 8. Best Practices - -### 8.1 Code Organization -- Use platform-specific directories for unique implementations -- Share common code through service interfaces -- Implement feature detection before using platform capabilities -- Keep platform-specific code isolated in dedicated directories -- Use TypeScript interfaces for cross-platform compatibility - -### 8.2 Platform Detection -```typescript -const platformService = PlatformServiceFactory.getInstance(); -const capabilities = platformService.getCapabilities(); - -if (capabilities.hasCamera) { - // Implement camera features -} -``` - -### 8.3 Feature Implementation -1. Define platform-agnostic interface -2. Create platform-specific implementations -3. Use factory pattern for instantiation -4. Implement graceful fallbacks -5. Add comprehensive error handling -6. Use dependency injection for better testability - -## 9. Dependency Management - -### 9.1 Platform-Specific Dependencies -```json -{ - "dependencies": { - "@capacitor/core": "^6.2.0", - "electron": "^33.2.1", - "vue": "^3.4.0" - } -} -``` - -### 9.2 Conditional Loading -```typescript -if (process.env.VITE_PLATFORM === 'capacitor') { - await import('@capacitor/core'); -} -``` - -## 10. Security Considerations - -### 10.1 Permission Handling -```typescript -async checkPermissions(): Promise { - if (platformService.isCapacitor()) { - return await checkNativePermissions(); - } - return await checkWebPermissions(); -} -``` - -### 10.2 Data Storage -- Use secure storage mechanisms for sensitive data -- Implement proper encryption for stored data -- Follow platform-specific security guidelines -- Regular security audits and updates - -This document should be updated as new features are added or platform-specific implementations change. Regular reviews ensure it remains current with the codebase. diff --git a/.cursor/rules/architecture/README.md b/.cursor/rules/architecture/README.md new file mode 100644 index 00000000..ac9669dd --- /dev/null +++ b/.cursor/rules/architecture/README.md @@ -0,0 +1,75 @@ +# Architecture Rules Directory + +**Author**: Matthew Raymer +**Date**: 2025-08-20 +**Status**: 🎯 **ACTIVE** - Architecture protection guidelines + +## Overview + +This directory contains MDC (Model Directive Configuration) rules that protect +critical architectural components of the TimeSafari project. These rules ensure +that changes to system architecture follow proper review, testing, and +documentation procedures. + +## Available Rules + +### Build Architecture Guard (`build_architecture_guard.mdc`) + +Protects the multi-platform build system including: + +- Vite configuration files +- Build scripts and automation +- Platform-specific configurations (iOS, Android, Electron, Web) +- Docker and deployment infrastructure +- CI/CD pipeline components + +**When to use**: Any time you're modifying build scripts, configuration files, +or deployment processes. + +**Authorization levels**: + +- **Level 1**: Minor changes (review required) +- **Level 2**: Moderate changes (testing required) +- **Level 3**: Major changes (ADR required) + +## Usage Guidelines + +### For Developers + +1. **Check the rule**: Before making architectural changes, review the relevant + rule +2. **Follow the process**: Use the appropriate authorization level +3. **Complete validation**: Run through the required checklist +4. **Update documentation**: Keep BUILDING.md and related docs current + +### For Reviewers + +1. **Verify authorization**: Ensure changes match the required level +2. **Check testing**: Confirm appropriate testing has been completed +3. **Validate documentation**: Ensure BUILDING.md reflects changes +4. **Assess risk**: Consider impact on other platforms and systems + +## Integration with Other Rules + +- **Version Control**: Works with `workflow/version_control.mdc` +- **Research & Diagnostic**: Supports `research_diagnostic.mdc` for + investigations +- **Software Development**: Aligns with development best practices +- **Markdown Automation**: Integrates with `docs/markdown-automation.mdc` for + consistent documentation formatting + +## Emergency Procedures + +If architectural changes cause system failures: + +1. **Immediate rollback** to last known working state +2. **Document the failure** with full error details +3. **Investigate root cause** using diagnostic workflows +4. **Update procedures** to prevent future failures + +--- + +**Status**: Active architecture protection +**Priority**: Critical +**Maintainer**: Development team +**Next Review**: 2025-09-20 diff --git a/.cursor/rules/architecture/build_architecture_guard.mdc b/.cursor/rules/architecture/build_architecture_guard.mdc new file mode 100644 index 00000000..1b07690b --- /dev/null +++ b/.cursor/rules/architecture/build_architecture_guard.mdc @@ -0,0 +1,186 @@ + +# Build Architecture Guard Directive + +**Author**: Matthew Raymer +**Date**: 2025-08-22 +**Status**: 🎯 **ACTIVE** - Build system protection guidelines + +## Purpose + +Protect the TimeSafari building architecture from unauthorized changes that +could break the multi-platform build pipeline, deployment processes, or +development workflow. This directive ensures all build system modifications +follow proper review, testing, and documentation procedures. + +**Note**: Recent Android build system enhancements (2025-08-22) include + sophisticated asset validation, platform-specific API routing, and automatic + resource regeneration. These features require enhanced testing and validation + procedures. + +## Protected Architecture Components + +### Core Build Infrastructure + +- **Vite Configuration Files**: `vite.config.*.mts` files + +- **Build Scripts**: All scripts in `scripts/` directory + +- **Package Scripts**: `package.json` build-related scripts + +- **Platform Configs**: `capacitor.config.ts`, `electron/`, `android/`, + + `ios/` + +- **Docker Configuration**: `Dockerfile`, `docker-compose.yml` + +- **Environment Files**: `.env.*`, `.nvmrc`, `.node-version` + +### Android-Specific Build Validation + +- **Asset Validation Scripts**: + + `validate_android_assets()` function and resource checking + +- **Resource Generation**: `capacitor-assets` integration and verification + +- **Platform-Specific IP Handling**: + + Android emulator vs physical device API routing + +- **Build Mode Validation**: Development/test/production mode handling + +- **Resource Fallback Logic**: + + Automatic regeneration of missing Android resources + +### Critical Build Dependencies + +- **Build Tools**: Vite, Capacitor, Electron, Android SDK, Xcode + +- **Asset Management**: `capacitor-assets.config.json`, asset scripts + +- **Testing Infrastructure**: Playwright, Jest, mobile test scripts + +- **CI/CD Pipeline**: GitHub Actions, build validation scripts + +- **Service Worker Assembly**: `sw_scripts/`, `sw_combine.js`, WASM copy steps + +## Change Authorization Requirements + +### Level 1: Minor Changes (Requires Review) + +- Documentation updates to `BUILDING.md` + +- Non-breaking script improvements + +- Test additions or improvements + +- Asset configuration updates + +**Process**: Code review + basic testing + +### Level 2: Moderate Changes (Requires Testing) + +- New build script additions + +- Environment variable changes + +- Dependency version updates + +- Platform-specific optimizations + +- **Build script argument parsing**: + + New flag handling (--api-ip, --auto-run, --deploy) + +- **Platform-specific environment overrides**: + + Android API server IP customization + +- **Asset regeneration logic**: Automatic fallback for missing Android resources + +**Process**: Code review + platform testing + documentation update + +### Level 3: Major Changes (Requires ADR) + +- Build system architecture changes + +- New platform support + +- Breaking changes to build scripts + +- Major dependency migrations + +**Process**: ADR creation + comprehensive testing + team review + +## Prohibited Actions + +### ❌ Never Allow Without ADR + +- **Delete or rename** core build scripts + +- **Modify** `package.json` build script names + +- **Change** Vite configuration structure + +- **Remove** platform-specific build targets + +- **Alter** Docker build process + +- **Modify** CI/CD pipeline without testing + +### ❌ Never Allow Without Testing + +- **Update** build dependencies + +- **Change** environment configurations + +- **Modify** asset generation scripts + +- **Alter** test infrastructure + +- **Update** platform SDK versions + +--- + +**See also**: + +- `.cursor/rules/architecture/build_validation.mdc` for + + detailed validation procedures + +- `.cursor/rules/architecture/build_testing.mdc` for testing requirements + +**Status**: Active build protection guidelines +**Priority**: Critical +**Estimated Effort**: Ongoing reference +**Dependencies**: None +**Stakeholders**: Development team, DevOps team, Build team + +**Estimated Effort**: Ongoing vigilance +**Dependencies**: All build system components +**Stakeholders**: Development team, DevOps, Platform owners +**Next Review**: 2025-09-22 + +## Model Implementation Checklist + +### Before Build Changes + +- [ ] **Change Level**: Determine if change is L1, L2, or L3 +- [ ] **Impact Assessment**: Assess impact on build system architecture +- [ ] **ADR Requirement**: Check if ADR is required for major changes +- [ ] **Testing Planning**: Plan appropriate testing for change level + +### During Build Changes + +- [ ] **Guard Compliance**: Ensure changes comply with build architecture guard +- [ ] **Documentation**: Document changes according to level requirements +- [ ] **Testing**: Execute appropriate testing for change level +- [ ] **Review Process**: Follow required review process for change level + +### After Build Changes + +- [ ] **Validation**: Verify build system still functions correctly +- [ ] **Documentation Update**: Update relevant documentation +- [ ] **Team Communication**: Communicate changes to affected teams +- [ ] **Monitoring**: Monitor for any build system issues diff --git a/.cursor/rules/architecture/build_testing.mdc b/.cursor/rules/architecture/build_testing.mdc new file mode 100644 index 00000000..4d0301dc --- /dev/null +++ b/.cursor/rules/architecture/build_testing.mdc @@ -0,0 +1,248 @@ +# Build Testing — Requirements and Emergency Procedures + +> **Agent role**: Reference this file for testing requirements and + emergency procedures when working with build architecture changes. + +## Emergency Procedures + +### Build System Broken + +1. **Immediate**: Revert to last known working commit + +2. **Investigation**: Create issue with full error details + +3. **Testing**: Verify all platforms work after revert + +4. **Documentation**: Update `BUILDING.md` with failure notes + +### Platform-Specific Failure + +1. **Isolate**: Identify which platform is affected + +2. **Test Others**: Verify other platforms still work + +3. **Rollback**: Revert platform-specific changes + +4. **Investigation**: Debug in isolated environment + +## Rollback Playbook + +### Immediate Rollback + +1. `git revert` or `git reset --hard `; restore prior `scripts/` or config + + files + +2. Rebuild affected targets; verify old behavior returns + +3. Post-mortem notes → update this guard and `BUILDING.md` if gaps found + +### Rollback Verification + +- **Web**: `npm run build:web:dev` and `npm run build:web:prod` + +- **Mobile**: `npm run build:android:test` and `npm run build:ios:test` + +- **Desktop**: `npm run build:electron:dev` and packaging commands + +- **Clean**: Run relevant `clean:*` scripts and verify re-build works + +### Android-Specific Rollback Verification + +- **Asset Generation**: `npm run build:android --assets` - + + verify resources regenerate + +- **API Routing**: Test both `--dev` and `--dev --api-ip ` modes + +- **Resource Validation**: + + Check `android/app/src/main/res/` for all required assets + +- **Build Modes**: Verify development, test, and production modes all work + +- **Resource Fallback**: + + Confirm missing resources trigger automatic regeneration + +## Integration Points + +### With Version Control + +- **Branch Protection**: Require reviews for build script changes + +- **Commit Messages**: Must reference ADR for major changes + +- **Testing**: All build changes must pass CI/CD pipeline + +### With Documentation + +- **BUILDING.md**: Must be updated for any script changes + +- **README.md**: Must reflect new build requirements + +- **CHANGELOG.md**: Must document breaking build changes + +### With Testing + +- **Pre-commit**: Run basic build validation + +- **CI/CD**: Full platform build testing + +- **Manual Testing**: Human verification of critical paths + +## Competence Hooks + +### Why This Works + +- **Prevents Build Failures**: Catches issues before they reach production + +- **Maintains Consistency**: Ensures all platforms build identically + +- **Reduces Debugging Time**: Prevents build system regressions + +### Common Pitfalls + +- **Silent Failures**: Changes that work on one platform but break others + +- **Dependency Conflicts**: Updates that create version incompatibilities + +- **Documentation Drift**: Build scripts that don't match documentation + +### Next Skill Unlock + +- Learn to test build changes across all platforms simultaneously + +### Teach-back + +- "What three platforms must I test before committing a build script change?" + +## Collaboration Hooks + +### Team Review Requirements + +- **Platform Owners**: iOS, Android, Electron, Web specialists + +- **DevOps**: CI/CD pipeline maintainers + +- **QA**: Testing infrastructure owners + +### Discussion Prompts + +- "Which platforms will be affected by this build change?" + +- "How can we test this change without breaking existing builds?" + +- "What's our rollback plan if this change fails?" + +## Self-Check (Before Allowing Changes) + +- [ ] **Authorization Level**: Is this change appropriate for the level? + +- [ ] **Testing Plan**: Is there a comprehensive testing strategy? + +- [ ] **Documentation**: Will BUILDING.md be updated? + +- [ ] **Rollback**: Is there a safe rollback mechanism? + +- [ ] **Team Review**: Have appropriate stakeholders been consulted? + +- [ ] **CI/CD**: Will this pass the build pipeline? + +## Continuous Improvement & Feedback + +### Feedback Collection + +The Build Architecture Guard system includes feedback mechanisms to continuously + improve its effectiveness: + +- **User Feedback**: Script includes feedback prompts for guard improvements + +- **Pattern Analysis**: + + Monitor which file patterns trigger false positives/negatives + +- **Documentation Gaps**: Track which changes lack proper documentation + +- **Testing Effectiveness**: Measure how often guard catches actual issues + +### Feedback Integration Process + +1. **Collect Feedback**: Monitor guard execution logs and user reports + +2. **Analyze Patterns**: Identify common false positives or missed patterns + +3. **Update Rules**: Modify `build_architecture_guard.mdc` based on feedback + +4. **Enhance Script**: Update `build-arch-guard.sh` with new validations + +5. **Test Changes**: Verify guard improvements don't introduce new issues + +6. **Document Updates**: Update guard documentation with new patterns + +### Feedback Categories + +- **False Positives**: Files flagged as sensitive that shouldn't be + +- **False Negatives**: Sensitive files that weren't caught + +- **Missing Patterns**: New file types that should be protected + +- **Overly Strict**: Patterns that are too restrictive + +- **Documentation Gaps**: Missing guidance for specific change types + +- **Testing Improvements**: Better validation procedures + +### Feedback Reporting + +When reporting guard issues, include: + +- **File patterns** that triggered false positives/negatives + +- **Build system changes** that weren't properly caught + +- **Documentation gaps** in current guard rules + +- **Testing procedures** that could be improved + +- **User experience** issues with guard enforcement + +--- + +**See also**: + +- `.cursor/rules/architecture/build_architecture_guard.mdc` for + + core protection guidelines + +- `.cursor/rules/architecture/build_validation.mdc` for validation procedures + +**Status**: Active testing requirements +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: build_architecture_guard.mdc, build_validation.mdc +**Stakeholders**: Development team, DevOps team, Build team + +## Model Implementation Checklist + +### Before Build Testing + +- [ ] **Test Planning**: Plan comprehensive testing strategy for build changes +- [ ] **Platform Coverage**: Identify all platforms that need testing +- [ ] **Risk Assessment**: Assess testing risks and mitigation strategies +- [ ] **Resource Planning**: Plan testing resources and time requirements + +### During Build Testing + +- [ ] **Test Execution**: Execute planned tests across all platforms +- [ ] **Issue Tracking**: Track and document any issues found +- [ ] **Feedback Collection**: Collect feedback on testing effectiveness +- [ ] **Documentation**: Document testing procedures and results + +### After Build Testing + +- [ ] **Result Analysis**: Analyze testing results and identify patterns +- [ ] **Feedback Integration**: Integrate feedback into testing procedures +- [ ] **Process Improvement**: Update testing procedures based on feedback +- [ ] **Team Communication**: Share testing results and improvements with team diff --git a/.cursor/rules/architecture/build_validation.mdc b/.cursor/rules/architecture/build_validation.mdc new file mode 100644 index 00000000..da78c63f --- /dev/null +++ b/.cursor/rules/architecture/build_validation.mdc @@ -0,0 +1,224 @@ +# Build Validation — Procedures and Requirements + +> **Agent role**: Reference this file for + detailed validation procedures when working with build architecture changes. + +## Required Validation Checklist + +### Before Any Build System Change + +- [ ] **Impact Assessment**: Which platforms are affected? + +- [ ] **Testing Plan**: How will this be tested across platforms? + +- [ ] **Rollback Plan**: How can this be reverted if it breaks? + +- [ ] **Documentation**: Will `BUILDING.md` need updates? + +- [ ] **Dependencies**: Are all required tools available? + +### After Build System Change + +- [ ] **Web Platform**: Does `npm run build:web:dev` work? + +- [ ] **Mobile Platforms**: Do iOS/Android builds succeed? + +- [ ] **Desktop Platform**: Does Electron build and run? + +- [ ] **Tests Pass**: Do all build-related tests pass? + +- [ ] **Documentation Updated**: Is `BUILDING.md` current? + +## Specific Test Commands (Minimum Required) + +### Web Platform + +- **Development**: `npm run build:web:dev` - serve and load app + +- **Production**: `npm run build:web:prod` - verify SW and WASM present + +### Mobile Platforms + +- **Android**: `npm run build:android:test` or `:prod` - confirm assets copied + +- **iOS**: `npm run build:ios:test` or `:prod` - verify build succeeds + +### Android Platform (Enhanced) + +- **Development Mode**: `npm run build:android --dev` - + + verify 10.0.2.2 API routing + +- **Custom IP Mode**: `npm run build:android --dev --api-ip 192.168.1.100` - + + verify custom IP + +- **Asset Validation**: `npm run build:android --assets` - + + verify resource generation + +- **Deploy Mode**: `npm run build:android --deploy` - verify device deployment + +### Desktop Platform + +- **Electron**: `npm run build:electron:dev` and packaging for target OS + +- **Verify**: Single-instance behavior and app boot + +### Auto-run (if affected) + +- **Test Mode**: `npm run auto-run:test` and platform variants + +- **Production Mode**: `npm run auto-run:prod` and platform variants + +### Clean and Rebuild + +- Run relevant `clean:*` scripts and ensure re-build works + +## Risk Matrix & Required Validation + +### Environment Handling + +- **Trigger**: Change to `.env.*` loading / variable names + +- **Validation**: Prove `dev/test/prod` builds; show environment echo in logs + +### Script Flow + +- **Trigger**: Reorder steps (prebuild → build → package), new flags + +- **Validation**: Dry-run + normal run, show exit codes & timing + +### Platform Packaging + +- **Trigger**: Electron NSIS/DMG/AppImage, Android/iOS bundle + +- **Validation**: Produce installer/artifact and open it; + + verify single-instance, + icons, signing + +### Service Worker / WASM + +- **Trigger**: `sw_combine.js`, WASM copy path + +- **Validation**: Verify combined SW exists and is injected; page loads offline; + + WASM present + +### Docker + +- **Trigger**: New base image, build args + +- **Validation**: Build image locally; run container; list produced `/dist` + +### Android Asset Management + +- **Trigger**: Changes to `validate_android_assets()` function or resource paths + +- **Validation**: + + Run `npm run build:android --assets` and verify all mipmap/drawable resources + +- **Risk**: Missing splash screens or app icons causing build failures + +### Android API Routing + +- **Trigger**: Changes to Android-specific API server IP logic + +- **Validation**: Test both emulator (10.0.2.2) and custom IP modes + +- **Risk**: API connectivity failures on different device types + +### Signing/Notarization + +- **Trigger**: Cert path/profiles + +- **Validation**: Show signing logs + verify on target OS + +## PR Template (Paste into Description) + +- [ ] **Level**: L1 / L2 / L3 + justification + +- [ ] **Files & platforms touched**: + +- [ ] **Risk triggers & mitigations**: + +- [ ] **Commands run (paste logs)**: + +- [ ] **Artifacts (names + sha256)**: + +- [ ] **Docs updated (sections/links)**: + +- [ ] **Rollback steps verified**: + +- [ ] **CI**: Jobs passing and artifacts uploaded + +## ADR Trigger List + +Raise an ADR when you propose any of: + +- **New build stage** or reorder of canonical stages + +- **Replacement of packager** / packaging format + +- **New environment model** or secure secret handling scheme + +- **New service worker assembly** strategy or cache policy + +- **New Docker base** or multi-stage pipeline + +- **Relocation of build outputs** or directory conventions + +- **New Android build modes** or argument parsing logic + +- **Changes to asset validation** or resource generation strategy + +- **Modifications to platform-specific API routing** ( + + Android emulator vs physical) + +- **New Android deployment strategies** or device management + +**ADR must include**: + motivation, alternatives, risks, validation plan, rollback, + doc diffs. + +--- + +**See also**: + +- `.cursor/rules/architecture/build_architecture_guard.mdc` for + + core protection guidelines + +- `.cursor/rules/architecture/build_testing.mdc` for testing requirements + +**Status**: Active validation procedures +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: build_architecture_guard.mdc +**Stakeholders**: Development team, DevOps team, Build team + +## Model Implementation Checklist + +### Before Build Changes + +- [ ] **Level Assessment**: Determine build validation level (L1/L2/L3) +- [ ] **Platform Analysis**: Identify all platforms affected by changes +- [ ] **Risk Assessment**: Identify risk triggers and mitigation strategies +- [ ] **Rollback Planning**: Plan rollback steps for build failures + +### During Build Implementation + +- [ ] **Validation Commands**: Run appropriate validation commands for level +- [ ] **Platform Testing**: Test changes across all affected platforms +- [ ] **Risk Mitigation**: Implement identified risk mitigation strategies +- [ ] **Documentation**: Document all commands run and their outputs + +### After Build Implementation + +- [ ] **Artifact Validation**: Verify build artifacts are correct and accessible +- [ ] **CI Verification**: Ensure CI jobs pass and artifacts are uploaded +- [ ] **Documentation Update**: Update relevant documentation sections +- [ ] **Team Communication**: Share build validation results with team diff --git a/.cursor/rules/camera-implementation.mdc b/.cursor/rules/camera-implementation.mdc deleted file mode 100644 index e7fef13c..00000000 --- a/.cursor/rules/camera-implementation.mdc +++ /dev/null @@ -1,222 +0,0 @@ ---- -description: -globs: -alwaysApply: false ---- -# Camera Implementation Documentation - -## Overview - -This document describes how camera functionality is implemented across the TimeSafari application. The application uses cameras for two main purposes: - -1. QR Code scanning -2. Photo capture - -## Components - -### QRScannerDialog.vue - -Primary component for QR code scanning in web browsers. - -**Key Features:** - -- Uses `qrcode-stream` for web-based QR scanning -- Supports both front and back cameras -- Provides real-time camera status feedback -- Implements error handling with user-friendly messages -- Includes camera switching functionality - -**Camera Access Flow:** - -1. Checks for camera API availability -2. Enumerates available video devices -3. Requests camera permissions -4. Initializes camera stream with preferred settings -5. Handles various error conditions with specific messages - -### PhotoDialog.vue - -Component for photo capture and selection. - -**Key Features:** - -- Cross-platform photo capture interface -- Image cropping capabilities -- File selection fallback -- Unified interface for different platforms - -## Services - -### QRScanner Services - -#### WebDialogQRScanner - -Web-based implementation of QR scanning. - -**Key Methods:** - -- `checkPermissions()`: Verifies camera permission status -- `requestPermissions()`: Requests camera access -- `isSupported()`: Checks for camera API support -- Handles various error conditions with specific messages - -#### CapacitorQRScanner - -Native implementation using Capacitor's MLKit. - -**Key Features:** - -- Uses `@capacitor-mlkit/barcode-scanning` -- Supports both front and back cameras -- Implements permission management -- Provides continuous scanning capability - -### Platform Services - -#### WebPlatformService - -Web-specific implementation of platform features. - -**Camera Capabilities:** - -- Uses HTML5 file input with capture attribute -- Falls back to file selection if camera unavailable -- Processes captured images for consistent format - -#### CapacitorPlatformService - -Native implementation using Capacitor. - -**Camera Features:** - -- Uses `Camera.getPhoto()` for native camera access -- Supports image editing -- Configures high-quality image capture -- Handles base64 image processing - -#### ElectronPlatformService - -Desktop implementation (currently unimplemented). - -**Status:** - -- Camera functionality not yet implemented -- Planned to use Electron's media APIs - -## Platform-Specific Considerations - -### iOS - -- Requires `NSCameraUsageDescription` in Info.plist -- Supports both front and back cameras -- Implements proper permission handling - -### Android - -- Requires camera permissions in manifest -- Supports both front and back cameras -- Handles permission requests through Capacitor - -### Web - -- Requires HTTPS for camera access -- Implements fallback mechanisms -- Handles browser compatibility issues - -## Error Handling - -### Common Error Scenarios - -1. No camera found -2. Permission denied -3. Camera in use by another application -4. HTTPS required -5. Browser compatibility issues - -### Error Response - -- User-friendly error messages -- Troubleshooting tips -- Clear instructions for resolution -- Platform-specific guidance - -## Security Considerations - -### Permission Management - -- Explicit permission requests -- Permission state tracking -- Graceful handling of denied permissions - -### Data Handling - -- Secure image processing -- Proper cleanup of camera resources -- No persistent storage of camera data - -## Best Practices - -### Camera Access - -1. Always check for camera availability -2. Request permissions explicitly -3. Handle all error conditions -4. Provide clear user feedback -5. Implement proper cleanup - -### Performance - -1. Optimize camera resolution -2. Implement proper resource cleanup -3. Handle camera switching efficiently -4. Manage memory usage - -### User Experience - -1. Clear status indicators -2. Intuitive camera controls -3. Helpful error messages -4. Smooth camera switching -5. Responsive UI feedback - -## Future Improvements - -### Planned Enhancements - -1. Implement Electron camera support -2. Add advanced camera features -3. Improve error handling -4. Enhance user feedback -5. Optimize performance - -### Known Issues - -1. Electron camera implementation pending -2. Some browser compatibility limitations -3. Platform-specific quirks to address - -## Dependencies - -### Key Packages - -- `@capacitor-mlkit/barcode-scanning` -- `qrcode-stream` -- `vue-picture-cropper` -- Platform-specific camera APIs - -## Testing - -### Test Scenarios - -1. Permission handling -2. Camera switching -3. Error conditions -4. Platform compatibility -5. Performance metrics - -### Test Environment - -- Multiple browsers -- iOS and Android devices -- Desktop platforms -- Various network conditions diff --git a/.cursor/rules/core/base_context.mdc b/.cursor/rules/core/base_context.mdc new file mode 100644 index 00000000..a1ad4bce --- /dev/null +++ b/.cursor/rules/core/base_context.mdc @@ -0,0 +1,217 @@ +--- +alwaysApply: false +--- +```json + +{ + "coaching_level": "standard", + "socratic_max_questions": 7, + "verbosity": "normal", + "timebox_minutes": null, + "format_enforcement": "strict" +} + +``` + +# Base Context — Human Competence First + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Core interaction guidelines + +## Purpose + +All interactions must *increase the human's competence over time* while +completing the task efficiently. The model may handle menial work and memory +extension, but must also promote learning, autonomy, and healthy work habits. +The model should also **encourage human interaction and collaboration** rather +than replacing it — outputs should be designed to **facilitate human discussion, +decision-making, and creativity**, not to atomize tasks into isolated, purely +machine-driven steps. + +## Principles + +1. Competence over convenience: finish the task *and* leave the human more + + capable next time. + +2. Mentorship, not lectures: be concise, concrete, and immediately applicable. + +3. Transparency: show assumptions, limits, and uncertainty; cite when + + non-obvious. + +4. Optional scaffolding: include small, skimmable learning hooks that do not + + bloat output. + +5. Time respect: default to **lean output**; offer opt-in depth via toggles. + +6. Psychological safety: encourage, never condescend; no medical/clinical + advice. No censorship! +7. Reusability: structure outputs so they can be saved, searched, reused, and + repurposed. +8. **Collaborative Bias**: Favor solutions that invite human review, + discussion, and iteration. When in doubt, ask "Who should this be shown + to?" or "Which human input would improve this?" + +## Toggle Definitions + +### coaching_level + +Determines the depth of learning support: `light` (short hooks), +`standard` (balanced), `deep` (detailed). + +### socratic_max_questions + +The number of clarifying questions the model may ask before proceeding. +If >0, questions should be targeted, minimal, and followed by reasonable +assumptions if unanswered. + +### verbosity + +'terse' (just a sentence), `concise` (minimum commentary), `normal` +(balanced explanation), or other project-defined levels. + +### timebox_minutes + +*integer or null* — When set to a positive integer (e.g., `5`), this acts +as a **time budget** guiding the model to prioritize delivering the most +essential parts of the task within that constraint. + +Behavior when set: + +1. **Prioritize Core Output** — Deliver the minimum viable solution or + + result first. + +2. **Limit Commentary** — Competence Hooks and Collaboration Hooks must be + + shorter than normal. + +3. **Signal Skipped Depth** — Omitted details should be listed under + + *Deferred for depth*. + +4. **Order by Value** — Start with blocking or high-value items, then + + proceed to nice-to-haves if budget allows. + +If `null`, there is no timebox — the model can produce full-depth +responses. + +### format_enforcement + +`strict` (reject outputs with format drift) or `relaxed` (minor deviations +acceptable). + +## Modes (select or combine) + +- **Doer**: produce the artifact fast, minimal commentary. + +- **Mentor**: add short "why/how" notes + next-step pointers. + +- **Socratic**: ask up to N targeted questions when requirements are + + ambiguous. + +- **Pair-Programmer/Pair-Writer**: explain tradeoffs as you implement. + +- **Facilitator**: structure output to be reviewable, commentable, and + + ready for group discussion. + +Default: Doer + short Mentor notes. + +## Competence & Collaboration Levers (keep lightweight) + +- "Why this works" (≤3 bullets) + +- "Common pitfalls" (≤3 bullets) + +- "Next skill unlock" (1 tiny action or reading) + +- "Teach-back" (1 sentence prompt the human can answer to self-check) + +- "Discussion prompts" (≤2 short questions for peers/stakeholders) + +## Output Contract (apply to every deliverable) + +- Clear **Objective** (1 line) + +- **Result** (artifact/code/answer) + +- **Use/Run** (how to apply/test) + +- **Competence Hooks** (the 4 learning levers above, kept terse) + +- **Collaboration Hooks** (discussion prompts or group review steps) + +- **Assumptions & Limits** + +- **References** (if used; links or titles) + +## Do-Not + +- No filler, hedging, or moralizing. + +- No medical/mental-health advice; keep "healthy habits" to general work + + practices. + +- No invented facts; mark uncertainty plainly. + +- No censorship. + +- Avoid outputs that bypass human review when such review is valuable. + +## Related Rulesets + +- **software_development.mdc**: For software-specific development practices + +- **research_diagnostic.mdc**: For investigation and research workflows + +## Model Implementation Checklist + +### Before Responding + +- [ ] **Toggle Review**: Check coaching_level, socratic_max_questions, verbosity, + timebox_minutes +- [ ] **Mode Selection**: Choose appropriate mode(s) for the task +- [ ] **Scope Understanding**: Clarify requirements and constraints +- [ ] **Context Analysis**: Review relevant rulesets and dependencies + +### During Response Creation + +- [ ] **Output Contract**: Include all required sections (Objective, Result, + Use/Run, etc.) +- [ ] **Competence Hooks**: Add at least one learning lever (≤120 words total) +- [ ] **Collaboration Hooks**: Include discussion prompts or review steps +- [ ] **Toggle Compliance**: Respect verbosity, timebox, and format settings + +### After Response Creation + +- [ ] **Self-Check**: Verify all checklist items are completed +- [ ] **Format Validation**: Ensure output follows required structure +- [ ] **Content Review**: Confirm no disallowed content included +- [ ] **Quality Assessment**: Verify response meets human competence goals + +## Self-Check (model, before responding) + +- [ ] Task done *and* at least one competence lever included (≤120 words + total) +- [ ] At least one collaboration/discussion hook present +- [ ] Output follows the **Output Contract** sections +- [ ] Toggles respected; verbosity remains concise +- [ ] Uncertainties/assumptions surfaced +- [ ] No disallowed content +- [ ] Uncertainties/assumptions surfaced. +- [ ] No disallowed content. + +--- + +**Status**: Active core guidelines +**Priority**: Critical +**Estimated Effort**: Ongoing reference +**Dependencies**: None (base ruleset) +**Stakeholders**: All AI interactions diff --git a/.cursor/rules/core/harbor_pilot_universal.mdc b/.cursor/rules/core/harbor_pilot_universal.mdc new file mode 100644 index 00000000..4f1da0f4 --- /dev/null +++ b/.cursor/rules/core/harbor_pilot_universal.mdc @@ -0,0 +1,202 @@ +```json + +{ + "coaching_level": "standard", + "socratic_max_questions": 2, + "verbosity": "concise", + "timebox_minutes": 10, + "format_enforcement": "strict" +} + +``` + +# Harbor Pilot Universal — Technical Guide Standards + +> **Agent role**: When creating technical guides, reference documents, or +> implementation plans, apply these universal directives to ensure consistent +> quality and structure. + +## Purpose + +- **Purpose fit**: Prioritizes human competence and collaboration while + delivering reproducible artifacts. + +- **Output Contract**: This directive **adds universal constraints** for any + technical topic while **inheriting** the Base Context contract sections. + +- **Toggles honored**: Uses the same toggle semantics; defaults above can be + overridden by the caller. + +## Core Directive + +Produce a **developer-grade, reproducible guide** for any technical topic +that onboards a competent practitioner **without meta narration** and **with +evidence-backed steps**. + +## Required Elements + +### 1. Time & Date Standards + +- Use **absolute dates** in **UTC** (e.g., `2025-08-21T14:22Z`) — avoid + "today/yesterday". + +- Include at least **one diagram** (Mermaid preferred). Choose the most + fitting type: + + - `sequenceDiagram` (protocols/flows), `flowchart`, `stateDiagram`, + `gantt` (timelines), or `classDiagram` (schemas). + +### 2. Evidence Requirements + +- **Reproducible Steps**: Every claim must have copy-paste commands + +- **Verifiable Outputs**: Include expected results, status codes, or + error messages + +- **Cite evidence** for *Works/Doesn't* items (timestamps, filenames, + line numbers, IDs/status codes, or logs). + +## Required Sections + +Follow this exact order **after** the Base Contract's **Objective → Result +→ Use/Run** headers: + +1. **Artifacts & Links** - Repos/PRs, design docs, datasets/HARs/pcaps, + scripts/tools, dashboards. + +2. **Environment & Preconditions** - OS/runtime, versions/build IDs, + services/endpoints/URLs, credentials/auth mode. + +3. **Architecture / Process Overview** - Short prose + **one diagram** + selected from the list above. + +4. **Interfaces & Contracts** - Choose one: API-based (endpoint table), + Data/Files (I/O contract), or Systems/Hardware (interfaces). + +5. **Repro: End-to-End Procedure** - Minimal copy-paste steps with + code/commands and **expected outputs**. +6. **What Works (with Evidence)** - Each item: **Time (UTC)** • + **Artifact/Req IDs** • **Status/Result** • **Where to verify**. +7. **What Doesn't (Evidence & Hypotheses)** - Each failure: locus, + evidence snippet; short hypothesis and **next probe**. +8. **Risks, Limits, Assumptions** - SLOs/limits, rate/size caps, + security boundaries, retries/backoff/idempotency patterns. +9. **Next Steps (Owner • Exit Criteria • Target Date)** - Actionable, + assigned, and time-bound. + +## Quality Standards + +### Do + +- **Do** quantify progress only against a defined scope with acceptance + criteria. + +- **Do** include minimal sample payloads/headers or I/O schemas; redact + sensitive values. + +- **Do** keep commentary lean; if timeboxed, move depth to **Deferred + for depth**. + +- **Do** use specific, actionable language that guides implementation. + +### Don't + +- **Don't** use marketing language or meta narration ("Perfect!", + "tool called", "new chat"). + +- **Don't** include IDE-specific chatter or internal rules unrelated to + the task. + +- **Don't** assume reader knowledge; provide context for all technical + decisions. + +## Model Implementation Checklist + +### Before Creating Technical Guides + +- [ ] **Scope Definition**: Clearly define problem, audience, and scope +- [ ] **Evidence Collection**: Gather specific timestamps, file references, and logs +- [ ] **Diagram Planning**: Plan appropriate diagram type for the technical process +- [ ] **Template Selection**: Choose relevant sections from required sections list + +### During Guide Creation + +- [ ] **Evidence Integration**: Include UTC timestamps and verifiable evidence +- [ ] **Diagram Creation**: Create Mermaid diagram that illustrates the process +- [ ] **Repro Steps**: Write copy-paste ready commands with expected outputs +- [ ] **Section Completion**: Fill in all required sections completely + +### After Guide Creation + +- [ ] **Validation**: Run through the validation checklist below +- [ ] **Evidence Review**: Verify all claims have supporting evidence +- [ ] **Repro Testing**: Test reproduction steps to ensure they work +- [ ] **Peer Review**: Share with technical leads for feedback + +## Validation Checklist + +Before publishing, verify: + +- [ ] **Diagram included** and properly formatted (Mermaid syntax valid) +- [ ] If API-based, **Auth** and **Key Headers/Params** are listed for + each endpoint +- [ ] **Environment section** includes all required dependencies and + versions +- [ ] Every Works/Doesn't item has **UTC timestamp**, **status/result**, + and **verifiable evidence** +- [ ] **Repro steps** are copy-paste ready with expected outputs +- [ ] Base **Output Contract** sections satisfied + (Objective/Result/Use/Run/Competence/Collaboration/Assumptions/References) + +## Integration Points + +### Base Context Integration + +- Apply historical comment management rules (see + + `.cursor/rules/development/historical_comment_management.mdc`) + +- Apply realistic time estimation rules (see + + `.cursor/rules/development/realistic_time_estimation.mdc`) + +### Competence Hooks + +- **Why this works**: Structured approach ensures completeness and + reproducibility + +- **Common pitfalls**: Skipping evidence requirements, vague language + +- **Next skill unlock**: Practice creating Mermaid diagrams for different + use cases + +- **Teach-back**: Explain how you would validate this guide's + reproducibility + +### Collaboration Hooks + +- **Reviewers**: Technical leads, subject matter experts + +- **Stakeholders**: Development teams, DevOps, QA teams + +--- + +**Status**: 🚢 ACTIVE — General ruleset extending *Base Context — Human +Competence First* + +**Priority**: Critical +**Estimated Effort**: Ongoing reference +**Dependencies**: base_context.mdc +**Stakeholders**: All AI interactions, Development teams + +## Example Diagram Template + +```mermaid + + + +``` + +**Note**: Replace the placeholder with an actual diagram that illustrates +the technical process, architecture, or workflow being documented. diff --git a/.cursor/rules/core/less_complex.mdc b/.cursor/rules/core/less_complex.mdc new file mode 100644 index 00000000..6c5ca71d --- /dev/null +++ b/.cursor/rules/core/less_complex.mdc @@ -0,0 +1,99 @@ + +alwaysApply: false + +--- + +# Minimalist Solution Principle (Cursor MDC) + +role: Engineering assistant optimizing for least-complex changes +focus: Deliver the smallest viable diff that fully resolves the current +bug/feature. Defer generalization unless justified with evidence. +language: Match repository languages and conventions + +## Rules + +1. **Default to the least complex solution.** Fix the problem directly + where it occurs; avoid new layers, indirection, or patterns unless + strictly necessary. +2. **Keep scope tight.** Implement only what is needed to satisfy the + acceptance criteria and tests for *this* issue. +3. **Avoid speculative abstractions.** Use the **Rule of Three**: + don't extract helpers/patterns until the third concrete usage proves + the shape. +4. **No drive-by refactors.** Do not rename, reorder, or reformat + unrelated code in the same change set. +5. **Minimize surface area.** Prefer local changes over cross-cutting + rewires; avoid new public APIs unless essential. +6. **Be dependency-frugal.** Do not add packages or services for + single, simple needs unless there's a compelling, documented reason. +7. **Targeted tests only.** Add the smallest set of tests that prove + the fix and guard against regression; don't rewrite suites. +8. **Document the "why enough."** Include a one-paragraph note + explaining why this minimal solution is sufficient *now*. + +## Future-Proofing Requires Evidence + Discussion + +Any added complexity "for the future" **must** include: + +- A referenced discussion/ADR (or issue link) summarizing the decision. +- **Substantial evidence**, e.g.: + - Recurring incidents or tickets that this prevents (list IDs). + - Benchmarks or profiling showing a real bottleneck. + - Concrete upcoming requirements with dates/owners, not hypotheticals. + - Risk assessment comparing maintenance cost vs. expected benefit. +- A clear trade-off table showing why minimal won't suffice. + +If this evidence is not available, **ship the minimal fix** and open a +follow-up discussion item. + +## PR / Change Checklist (enforced by reviewer + model) + +- [ ] Smallest diff that fully fixes the issue (attach `git diff --stat` + if useful). +- [ ] No unrelated refactors or formatting. +- [ ] No new dependencies, or justification + ADR link provided. +- [ ] Abstractions only if ≥3 call sites or strong evidence says + otherwise (cite). +- [ ] Targeted tests proving the fix/regression guard. +- [ ] Short "Why this is enough now" note in the PR description. +- [ ] Optional: "Future Work (non-blocking)" section listing deferred + ideas. + +## Assistant Output Contract + +When proposing a change, provide: + +1. **Minimal Plan**: 3–6 bullet steps scoped to the immediate fix. +2. **Patch Sketch**: Focused diffs/snippets touching only necessary + files. +3. **Risk & Rollback**: One paragraph each on risk, quick rollback, + and test points. +4. **(If proposing complexity)**: Link/inline ADR summary + evidence + + trade-offs; otherwise default to minimal. + + One paragraph each on risk, quick rollback, and test points. +5. **(If proposing complexity)**: Link/inline ADR summary + evidence + + trade-offs; otherwise default to minimal. + +## Model Implementation Checklist + +### Before Proposing Changes + +- [ ] **Problem Analysis**: Clearly understand the specific issue scope +- [ ] **Evidence Review**: Gather evidence that justifies the change +- [ ] **Complexity Assessment**: Evaluate if change requires added complexity +- [ ] **Alternative Research**: Consider simpler solutions first + +### During Change Design + +- [ ] **Minimal Scope**: Design solution that addresses only the current issue +- [ ] **Evidence Integration**: Include specific evidence for any complexity +- [ ] **Dependency Review**: Minimize new dependencies and packages +- [ ] **Testing Strategy**: Plan minimal tests that prove the fix + +### After Change Design + +- [ ] **Self-Review**: Verify solution follows minimalist principles +- [ ] **Evidence Validation**: Confirm all claims have supporting evidence +- [ ] **Complexity Justification**: Document why minimal approach suffices +- [ ] **Future Work Planning**: Identify deferred improvements for later diff --git a/.cursor/rules/absurd-sql.mdc b/.cursor/rules/database/absurd-sql.mdc similarity index 66% rename from .cursor/rules/absurd-sql.mdc rename to .cursor/rules/database/absurd-sql.mdc index 56729c2a..9f959dfc 100644 --- a/.cursor/rules/absurd-sql.mdc +++ b/.cursor/rules/database/absurd-sql.mdc @@ -1,105 +1,168 @@ --- -description: -globs: -alwaysApply: true +globs: **/db/databaseUtil.ts, **/interfaces/absurd-sql.d.ts, + **/src/registerSQLWorker.js, **/ +services/AbsurdSqlDatabaseService.ts +alwaysApply: false --- + # Absurd SQL - Cursor Development Guide +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Database development guidelines + ## Project Overview -Absurd SQL is a backend implementation for sql.js that enables persistent SQLite databases in the browser by using IndexedDB as a block storage system. This guide provides rules and best practices for developing with this project in Cursor. + +Absurd SQL is a backend implementation for sql.js that enables persistent +SQLite databases in the browser by using IndexedDB as a block storage system. +This guide provides rules and best practices for developing with this project +in Cursor. ## Project Structure + ``` + absurd-sql/ ├── src/ # Source code ├── dist/ # Built files ├── package.json # Dependencies and scripts ├── rollup.config.js # Build configuration └── jest.config.js # Test configuration + ``` ## Development Rules ### 1. Worker Thread Requirements + - All SQL operations MUST be performed in a worker thread + - Main thread should only handle worker initialization and communication + - Never block the main thread with database operations ### 2. Code Organization + - Keep worker code in separate files (e.g., `*.worker.js`) + - Use ES modules for imports/exports + - Follow the project's existing module structure ### 3. Required Headers + When developing locally or deploying, ensure these headers are set: + ``` + Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp + ``` ### 4. Browser Compatibility + - Primary target: Modern browsers with SharedArrayBuffer support + - Fallback mode: Safari (with limitations) + - Always test in both modes ### 5. Database Configuration + Recommended database settings: + ```sql + PRAGMA journal_mode=MEMORY; PRAGMA page_size=8192; -- Optional, but recommended + ``` ### 6. Development Workflow + 1. Install dependencies: + ```bash + yarn add @jlongster/sql.js absurd-sql + ``` 2. Development commands: + - `yarn build` - Build the project + - `yarn jest` - Run tests + - `yarn serve` - Start development server ### 7. Testing Guidelines + - Write tests for both SharedArrayBuffer and fallback modes + - Use Jest for testing + - Include performance benchmarks for critical operations ### 8. Performance Considerations + - Use bulk operations when possible + - Monitor read/write performance + - Consider using transactions for multiple operations + - Avoid unnecessary database connections ### 9. Error Handling + - Implement proper error handling for: + - Worker initialization failures + - Database connection issues + - Concurrent access conflicts (in fallback mode) + - Storage quota exceeded scenarios ### 10. Security Best Practices + - Never expose database operations directly to the client + - Validate all SQL queries + - Implement proper access controls + - Handle sensitive data appropriately ### 11. Code Style + - Follow ESLint configuration + - Use async/await for asynchronous operations + - Document complex database operations + - Include comments for non-obvious optimizations ### 12. Debugging + - Use `jest-debug` for debugging tests + - Monitor IndexedDB usage in browser dev tools + - Check worker communication in console + - Use performance monitoring tools ## Common Patterns ### Worker Initialization + ```javascript + // Main thread import { initBackend } from 'absurd-sql/dist/indexeddb-main-thread'; @@ -107,10 +170,13 @@ function init() { let worker = new Worker(new URL('./index.worker.js', import.meta.url)); initBackend(worker); } + ``` ### Database Setup + ```javascript + // Worker thread import initSqlJs from '@jlongster/sql.js'; import { SQLiteFS } from 'absurd-sql'; @@ -120,34 +186,88 @@ async function setupDatabase() { let SQL = await initSqlJs({ locateFile: file => file }); let sqlFS = new SQLiteFS(SQL.FS, new IndexedDBBackend()); SQL.register_for_idb(sqlFS); - + SQL.FS.mkdir('/sql'); SQL.FS.mount(sqlFS, {}, '/sql'); - + return new SQL.Database('/sql/db.sqlite', { filename: true }); } + ``` ## Troubleshooting ### Common Issues + 1. SharedArrayBuffer not available + - Check COOP/COEP headers + - Verify browser support + - Test fallback mode 2. Worker initialization failures + - Check file paths + - Verify module imports + - Check browser console for errors 3. Performance issues + - Monitor IndexedDB usage + - Check for unnecessary operations + - Verify transaction usage ## Resources + - [Project Demo](https://priceless-keller-d097e5.netlify.app/) + - [Example Project](https://github.com/jlongster/absurd-example-project) + - [Blog Post](https://jlongster.com/future-sql-web) -- [SQL.js Documentation](https://github.com/sql-js/sql.js/) \ No newline at end of file + +- [SQL.js Documentation](https://github.com/sql-js/sql.js/) + +--- + +**Status**: Active database development guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: Absurd SQL, SQL.js, IndexedDB +**Stakeholders**: Development team, Database team + +- [Project Demo](https://priceless-keller-d097e5.netlify.app/) + +- [Example Project](https://github.com/jlongster/absurd-example-project) + +- [Blog Post](https://jlongster.com/future-sql-web) + +- [SQL.js Documentation](https://github.com/sql-js/sql.js/) + +## Model Implementation Checklist + +### Before Absurd SQL Implementation + +- [ ] **Browser Support**: Verify SharedArrayBuffer and COOP/COEP support +- [ ] **Worker Setup**: Plan worker thread initialization and communication +- [ ] **Database Planning**: Plan database schema and initialization +- [ ] **Performance Planning**: Plan performance monitoring and optimization + +### During Absurd SQL Implementation + +- [ ] **Worker Initialization**: Set up worker threads with proper communication +- [ ] **Database Setup**: Initialize SQLite database with IndexedDB backend +- [ ] **File System**: Configure SQLiteFS with proper mounting +- [ ] **Error Handling**: Implement proper error handling for worker failures + +### After Absurd SQL Implementation + +- [ ] **Cross-Browser Testing**: Test across different browsers and devices +- [ ] **Performance Validation**: Monitor IndexedDB usage and performance +- [ ] **Worker Validation**: Verify worker communication and database operations +- [ ] **Documentation**: Update Absurd SQL implementation documentation diff --git a/.cursor/rules/database/legacy_dexie.mdc b/.cursor/rules/database/legacy_dexie.mdc new file mode 100644 index 00000000..ffb9e8f3 --- /dev/null +++ b/.cursor/rules/database/legacy_dexie.mdc @@ -0,0 +1,62 @@ +# Legacy Dexie Database — Migration Guidelines + +> **Agent role**: Reference this file when working with legacy Dexie +> database code or migration patterns. + +## Overview + +All references in the codebase to Dexie apply only to migration from +IndexedDb to Absurd SQL. Dexie is no longer used for new development. + +## Migration Status + +- **Legacy Code**: Existing Dexie implementations being migrated +- **Target**: Absurd SQL with IndexedDB backend +- **Timeline**: Gradual migration as features are updated + +## Key Principles + +- **No New Dexie**: All new database operations use Absurd SQL +- **Migration Path**: Legacy code should be migrated when updated +- **Backward Compatibility**: Maintain existing functionality during + migration + +## Integration Points + +- Apply these rules when updating database-related code +- Use during feature development and refactoring +- Include in database architecture decisions + +--- + +**Status**: Legacy migration guidelines +**Priority**: Low +**Estimated Effort**: Ongoing reference +**Dependencies**: absurd-sql.mdc +**Stakeholders**: Database team, Development team + +All references in the codebase to Dexie apply only to migration from IndexedDb +to Sqlite and will be deprecated in future versions. + +## Model Implementation Checklist + +### Before Legacy Dexie Work + +- [ ] **Migration Analysis**: Identify legacy Dexie code that needs migration +- [ ] **Target Planning**: Plan migration to Absurd SQL with IndexedDB backend +- [ ] **Backward Compatibility**: Plan to maintain existing functionality +- [ ] **Testing Strategy**: Plan testing approach for migration + +### During Legacy Dexie Migration + +- [ ] **No New Dexie**: Ensure no new Dexie code is introduced +- [ ] **Migration Implementation**: Implement migration to Absurd SQL +- [ ] **Functionality Preservation**: Maintain existing functionality during migration +- [ ] **Error Handling**: Implement proper error handling for migration + +### After Legacy Dexie Migration + +- [ ] **Functionality Testing**: Verify all functionality still works correctly +- [ ] **Performance Validation**: Ensure performance meets or exceeds legacy +- [ ] **Documentation Update**: Update database documentation +- [ ] **Legacy Cleanup**: Remove deprecated Dexie code diff --git a/.cursor/rules/development/asset_configuration.mdc b/.cursor/rules/development/asset_configuration.mdc new file mode 100644 index 00000000..a53e9ffb --- /dev/null +++ b/.cursor/rules/development/asset_configuration.mdc @@ -0,0 +1,105 @@ +--- +description: when doing anything with capacitor assets +alwaysApply: false +--- + +# Asset Configuration Directive + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Asset management guidelines + +*Scope: Assets Only (icons, splashes, image pipelines) — not overall build +orchestration* + +## Intent + +- Version **asset configuration files** (optionally dev-time generated). + +- **Do not** version platform asset outputs (Android/iOS/Electron); generate + + them **at build-time** with standard tools. + +- Keep existing per-platform build scripts unchanged. + +## Source of Truth + +- **Preferred (Capacitor default):** `resources/` as the single master source. + +- **Alternative:** `assets/` is acceptable **only** if `capacitor-assets` is + + explicitly configured to read from it. + +- **Never** maintain both `resources/` and `assets/` as parallel sources. + + Migrate and delete the redundant folder. + +## Config Files + +- Live under: `config/assets/` (committed). + +- Examples: + + - `config/assets/capacitor-assets.config.json` (or the path the tool + + expects) + + - `config/assets/android.assets.json` + + - `config/assets/ios.assets.json` + + - `config/assets/common.assets.yaml` (optional shared layer) + +- **Dev-time generation allowed** for these configs; **build-time + + generation is forbidden**. + +## Build-Time Behavior + +- Build generates platform assets (not configs) using the standard chain: + +```bash + +npm run build:capacitor # web build via Vite (.mts) +npx cap sync +npx capacitor-assets generate # produces platform assets; not committed + +# then platform-specific build steps + +``` + +--- + +**Status**: Active asset management directive +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: capacitor-assets toolchain +**Stakeholders**: Development team, Build team + + npx capacitor-assets generate # produces platform assets; not committed + +# then platform-specific build steps + +## Model Implementation Checklist + +### Before Asset Configuration + +- [ ] **Source Review**: Identify current asset source location (`resources/` or + `assets/`) +- [ ] **Tool Assessment**: Verify capacitor-assets toolchain is available +- [ ] **Config Planning**: Plan configuration file structure and location +- [ ] **Platform Analysis**: Understand asset requirements for all target platforms + +### During Asset Configuration + +- [ ] **Source Consolidation**: Ensure single source of truth (prefer `resources/`) +- [ ] **Config Creation**: Create platform-specific asset configuration files +- [ ] **Tool Integration**: Configure capacitor-assets to read from correct source +- [ ] **Build Integration**: Integrate asset generation into build pipeline + +### After Asset Configuration + +- [ ] **Build Testing**: Verify assets generate correctly at build time +- [ ] **Platform Validation**: Test asset generation across all platforms +- [ ] **Documentation**: Update build documentation with asset generation steps +- [ ] **Team Communication**: Communicate asset workflow changes to team diff --git a/.cursor/rules/development/complexity_assessment.mdc b/.cursor/rules/development/complexity_assessment.mdc new file mode 100644 index 00000000..02b97560 --- /dev/null +++ b/.cursor/rules/development/complexity_assessment.mdc @@ -0,0 +1,177 @@ +# Complexity Assessment — Evaluation Frameworks + +> **Agent role**: Reference this file for + complexity evaluation frameworks when assessing project complexity. + +## 📊 Complexity Assessment Framework + +### **Technical Complexity Factors** + +#### **Code Changes** + +- **Simple**: Text, styling, configuration updates + +- **Medium**: New components, refactoring existing code + +- **Complex**: Architecture changes, new patterns, integrations + +- **Unknown**: New technologies, APIs, or approaches + +#### **Platform Impact** + +- **Single platform**: Web-only or mobile-only changes + +- **Two platforms**: Web + mobile or web + desktop + +- **Three platforms**: Web + mobile + desktop + +- **Cross-platform consistency**: Ensuring behavior matches across all platforms + +#### **Testing Requirements** + +- **Basic**: Unit tests for new functionality + +- **Comprehensive**: Integration tests, cross-platform testing + +- **User acceptance**: User testing, feedback integration + +- **Performance**: Load testing, optimization validation + +### **Dependency Complexity** + +#### **Internal Dependencies** + +- **Low**: Self-contained changes, no other components affected + +- **Medium**: Changes affect related components or services + +- **High**: Changes affect core architecture or multiple systems + +- **Critical**: Changes affect data models or core business logic + +#### **External Dependencies** + +- **None**: No external services or APIs involved + +- **Low**: Simple API calls or service integrations + +- **Medium**: Complex integrations with external systems + +- **High**: Third-party platform dependencies or complex APIs + +#### **Infrastructure Dependencies** + +- **None**: No infrastructure changes required + +- **Low**: Configuration updates or environment changes + +- **Medium**: New services or infrastructure components + +- **High**: Platform migrations or major infrastructure changes + +## 🔍 Complexity Evaluation Process + +### **Step 1: Technical Assessment** + +1. **Identify scope of changes** - what files/components are affected + +2. **Assess platform impact** - which platforms need updates + +3. **Evaluate testing needs** - what testing is required + +4. **Consider performance impact** - will this affect performance + +### **Step 2: Dependency Mapping** + +1. **Map internal dependencies** - what other components are affected + +2. **Identify external dependencies** - what external services are involved + +3. **Assess infrastructure needs** - what infrastructure changes are required + +4. **Evaluate risk factors** - what could go wrong + +### **Step 3: Complexity Classification** + +1. **Assign complexity levels** to each factor + +2. **Identify highest complexity** areas that need attention + +3. **Plan mitigation strategies** for high-complexity areas + +4. **Set realistic expectations** based on complexity assessment + +## 📋 Complexity Assessment Checklist + +- [ ] Technical scope identified and mapped + +- [ ] Platform impact assessed across all targets + +- [ ] Testing requirements defined and planned + +- [ ] Internal dependencies mapped and evaluated + +- [ ] External dependencies identified and assessed + +- [ ] Infrastructure requirements evaluated + +- [ ] Risk factors identified and mitigation planned + +- [ ] Complexity levels assigned to all factors + +- [ ] Realistic expectations set based on assessment + +## 🎯 Complexity Reduction Strategies + +### **Scope Reduction** + +- Break large features into smaller, manageable pieces + +- Focus on core functionality first, add polish later + +- Consider phased rollout to reduce initial complexity + +### **Dependency Management** + +- Minimize external dependencies when possible + +- Use abstraction layers to isolate complex integrations + +- Plan for dependency failures and fallbacks + +### **Testing Strategy** + +- Start with basic testing and expand coverage + +- Use automated testing to reduce manual testing complexity + +- Plan for iterative testing and feedback cycles + +## **See also** + +- `.cursor/rules/development/realistic_time_estimation.mdc` for the core principles + +- `.cursor/rules/development/planning_examples.mdc` for planning examples + +## Model Implementation Checklist + +### Before Complexity Assessment + +- [ ] **Problem Scope**: Clearly define the problem to be assessed +- [ ] **Stakeholder Identification**: Identify all parties affected by complexity +- [ ] **Context Analysis**: Understand technical and business context +- [ ] **Assessment Criteria**: Define what factors determine complexity + +### During Complexity Assessment + +- [ ] **Technical Mapping**: Map technical scope and platform impact +- [ ] **Dependency Analysis**: Identify internal and external dependencies +- [ ] **Risk Evaluation**: Assess infrastructure needs and risk factors +- [ ] **Complexity Classification**: Assign complexity levels to all factors + +### After Complexity Assessment + +- [ ] **Mitigation Planning**: Plan strategies for high-complexity areas +- [ ] **Expectation Setting**: Set realistic expectations based on assessment +- [ ] **Documentation**: Document assessment process and findings +- [ ] **Stakeholder Communication**: Share results and recommendations diff --git a/.cursor/rules/development/dependency_management.mdc b/.cursor/rules/development/dependency_management.mdc new file mode 100644 index 00000000..a92f0709 --- /dev/null +++ b/.cursor/rules/development/dependency_management.mdc @@ -0,0 +1,177 @@ +# Dependency Management — Best Practices + +> **Agent role**: Reference this file for dependency management strategies and + best practices when working with software projects. + +## Dependency Management Best Practices + +### Pre-build Validation + +- **Check Critical Dependencies**: + + Validate essential tools before executing build + scripts + +- **Use npx for Local Dependencies**: Prefer `npx tsx` over direct `tsx` to + + avoid PATH issues + +- **Environment Consistency**: Ensure all team members have identical dependency + + versions + +### Common Pitfalls + +- **Missing npm install**: Team members cloning without running `npm install` + +- **PATH Issues**: Direct command execution vs. npm script execution differences + +- **Version Mismatches**: Different Node.js/npm versions across team members + +### Validation Strategies + +- **Dependency Check Scripts**: Implement pre-build validation for critical + + dependencies + +- **Environment Requirements**: + + Document and enforce minimum Node.js/npm versions + +- **Onboarding Checklist**: Standardize team member setup procedures + +### Error Messages and Guidance + +- **Specific Error Context**: + + Provide clear guidance when dependency issues occur + +- **Actionable Solutions**: Direct users to specific commands (`npm install`, + + `npm run check:dependencies`) + +- **Environment Diagnostics**: Implement comprehensive environment validation + + tools + +### Build Script Enhancements + +- **Early Validation**: Check dependencies before starting build processes + +- **Graceful Degradation**: Continue builds when possible but warn about issues + +- **Helpful Tips**: Remind users about dependency management best practices + +## Environment Setup Guidelines + +### Required Tools + +- **Node.js**: Minimum version requirements and LTS recommendations + +- **npm**: Version compatibility and global package management + +- **Platform-specific tools**: Android SDK, Xcode, etc. + +### Environment Variables + +- **NODE_ENV**: Development, testing, production environments + +- **PATH**: Ensure tools are accessible from command line + +- **Platform-specific**: Android SDK paths, Xcode command line tools + +### Validation Commands + +```bash + +# Check Node.js version + +node --version + +# Check npm version + +npm --version + +# Check global packages + +npm list -g --depth=0 + +# Validate platform tools + +npx capacitor doctor + +``` + +## Dependency Troubleshooting + +### Common Issues + +1. **Permission Errors**: Use `sudo` sparingly, prefer `npm config set prefix` + +2. **Version Conflicts**: Use `npm ls` to identify dependency conflicts + +3. **Cache Issues**: Clear npm cache with `npm cache clean --force` + +4. **Lock File Issues**: Delete `package-lock.json` and `node_modules`, + + then reinstall + +### Resolution Strategies + +- **Dependency Audit**: Run `npm audit` to identify security issues + +- **Version Pinning**: Use exact versions for critical dependencies + +- **Peer Dependency Management**: Ensure compatible versions across packages + +- **Platform-specific Dependencies**: Handle different requirements per platform + +## Best Practices for Teams + +### Onboarding + +- **Environment Setup Script**: Automated setup for new team members + +- **Version Locking**: Use `package-lock.json` and `yarn.lock` consistently + +- **Documentation**: Clear setup instructions with troubleshooting steps + +### Maintenance + +- **Regular Updates**: Schedule dependency updates and security patches + +- **Testing**: Validate changes don't break existing functionality + +- **Rollback Plan**: Maintain ability to revert to previous working versions + +**See also**: + `.cursor/rules/development/software_development.mdc` for core development principles. + +**Status**: Active dependency management guidelines +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: software_development.mdc +**Stakeholders**: Development team, DevOps team + +## Model Implementation Checklist + +### Before Dependency Changes + +- [ ] **Current State Review**: Check current dependency versions and status +- [ ] **Impact Analysis**: Assess impact of dependency changes on codebase +- [ ] **Compatibility Check**: Verify compatibility with existing code +- [ ] **Security Review**: Review security implications of dependency changes + +### During Dependency Management + +- [ ] **Version Selection**: Choose appropriate dependency versions +- [ ] **Testing**: Test with new dependency versions +- [ ] **Documentation**: Update dependency documentation +- [ ] **Team Communication**: Communicate changes to team members + +### After Dependency Changes + +- [ ] **Comprehensive Testing**: Test all functionality with new dependencies +- [ ] **Documentation Update**: Update all relevant documentation +- [ ] **Deployment Planning**: Plan and execute deployment strategy +- [ ] **Monitoring**: Monitor for issues after deployment diff --git a/.cursor/rules/development/development_guide.mdc b/.cursor/rules/development/development_guide.mdc new file mode 100644 index 00000000..092ffb98 --- /dev/null +++ b/.cursor/rules/development/development_guide.mdc @@ -0,0 +1,33 @@ +--- +globs: **/src/**/* +alwaysApply: false +--- +✅ use system date command to timestamp all interactions with accurate date and + time +✅ python script files must always have a blank line at their end +✅ remove whitespace at the end of lines +✅ use npm run lint-fix to check for warnings +✅ do not use npm run dev let me handle running and supplying feedback + +## Model Implementation Checklist + +### Before Development Work + +- [ ] **System Date Check**: Use system date command for accurate timestamps +- [ ] **Environment Setup**: Verify development environment is ready +- [ ] **Linting Setup**: Ensure npm run lint-fix is available +- [ ] **Code Standards**: Review project coding standards and requirements + +### During Development + +- [ ] **Timestamp Usage**: Include accurate timestamps in all interactions +- [ ] **Code Quality**: Use npm run lint-fix to check for warnings +- [ ] **File Standards**: Ensure Python files have blank line at end +- [ ] **Whitespace**: Remove trailing whitespace from all lines + +### After Development + +- [ ] **Linting Check**: Run npm run lint-fix to verify code quality +- [ ] **File Validation**: Confirm Python files end with blank line +- [ ] **Whitespace Review**: Verify no trailing whitespace remains +- [ ] **Documentation**: Update relevant documentation with changes diff --git a/.cursor/rules/development/historical_comment_management.mdc b/.cursor/rules/development/historical_comment_management.mdc new file mode 100644 index 00000000..9634e9d0 --- /dev/null +++ b/.cursor/rules/development/historical_comment_management.mdc @@ -0,0 +1,119 @@ +# Historical Comment Management — Code Clarity Guidelines + +> **Agent role**: When encountering historical comments about removed +> methods, deprecated patterns, or architectural changes, apply these +> guidelines to maintain code clarity and developer guidance. + +## Overview + +Historical comments should either be **removed entirely** or **transformed +into actionable guidance** for future developers. Avoid keeping comments +that merely state what was removed without explaining why or what to do +instead. + +## Decision Framework + +### When to Remove Comments + +- **Obsolete Information**: Comment describes functionality that no + longer exists +- **Outdated Context**: Comment refers to old patterns that are no + longer relevant +- **No Actionable Value**: Comment doesn't help future developers + make decisions + +### When to Transform Comments + +- **Migration Guidance**: Future developers might need to understand + the evolution +- **Alternative Approaches**: The comment can guide future + implementation choices +- **Historical Context**: Understanding the change helps with + current decisions + +## Transformation Patterns + +### 1. **Removed Method** → **Alternative Approach** + +```typescript +// Before: Historical comment +// turnOffNotifyingFlags method removed - notification state is now +// managed by NotificationSection component + +// After: Actionable guidance +// Note: Notification state management has been migrated to +// NotificationSection component +``` + +### 2. **Deprecated Pattern** → **Current Best Practice** + +```typescript +// Before: Historical comment +// Database access has been migrated from direct IndexedDB calls to +// PlatformServiceMixin + +// After: Actionable guidance +// This provides better platform abstraction and consistent error +// handling across web/mobile/desktop + +// When adding new database operations, use this.$getContact(), +// this.$saveSettings(), etc. +``` + +## Best Practices + +### 1. **Use Actionable Language**: Guide future decisions, not just + +document history + +### 2. **Provide Alternatives**: Always suggest what to use instead + +### 3. **Update Related Docs**: If removing from code, consider + +adding to documentation + +### 4. **Keep Context**: Include enough information to understand + +why the change was made + +## Integration Points + +- Apply these rules when reviewing code changes +- Use during code cleanup and refactoring +- Include in code review checklists + +--- + +**See also**: + +- `.cursor/rules/development/historical_comment_patterns.mdc` for detailed + transformation examples and patterns + +**Status**: Active comment management guidelines +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: None +**Stakeholders**: Development team, Code reviewers + +## Model Implementation Checklist + +### Before Comment Review + +- [ ] **Code Analysis**: Review code for historical or outdated comments +- [ ] **Context Understanding**: Understand the current state of the codebase +- [ ] **Pattern Identification**: Identify comments that need transformation or removal +- [ ] **Documentation Planning**: Plan where to move important historical context + +### During Comment Management + +- [ ] **Transformation**: Convert historical comments to actionable guidance +- [ ] **Alternative Provision**: Suggest current best practices and alternatives +- [ ] **Context Preservation**: Maintain enough information to understand changes +- [ ] **Documentation Update**: Move important context to appropriate documentation + +### After Comment Management + +- [ ] **Code Review**: Ensure transformed comments provide actionable value +- [ ] **Documentation Sync**: Verify related documentation is updated +- [ ] **Team Communication**: Share comment transformation patterns with team +- [ ] **Process Integration**: Include comment management in code review checklists diff --git a/.cursor/rules/development/historical_comment_patterns.mdc b/.cursor/rules/development/historical_comment_patterns.mdc new file mode 100644 index 00000000..47dc60bb --- /dev/null +++ b/.cursor/rules/development/historical_comment_patterns.mdc @@ -0,0 +1,139 @@ +# Historical Comment Patterns — Transformation Examples + +> **Agent role**: Reference this file for specific patterns and + examples when transforming historical comments into actionable guidance. + +## 🔄 Transformation Patterns + +### 1. From Removal Notice to Migration Note + +```typescript + +// ❌ REMOVE THIS +// turnOffNotifyingFlags method removed - + notification state is now managed by NotificationSection component + +// ✅ TRANSFORM TO THIS +// Note: Notification state management has been migrated to NotificationSection + component +// which handles its own lifecycle and persistence via PlatformServiceMixin + +``` + +### 2. From Deprecation Notice to Implementation Guide + +```typescript + +// ❌ REMOVE THIS +// This will be handled by the NewComponent now +// No need to call oldMethod() as it's no longer needed + +// ✅ TRANSFORM TO THIS +// Note: This functionality has been migrated to NewComponent +// which provides better separation of concerns and testability + +``` + +### 3. From Historical Note to Architectural Context + +```typescript + +// ❌ REMOVE THIS +// Old approach: used direct database calls +// New approach: uses service layer + +// ✅ TRANSFORM TO THIS +// Note: Database access has been abstracted through service layer +// for better testability and platform independence + +``` + +## 🚫 Anti-Patterns to Remove + +- Comments that only state what was removed + +- Comments that don't explain the current approach + +- Comments that reference non-existent methods + +- Comments that are self-evident from the code + +- Comments that don't help future decision-making + +## 📚 Examples + +### Good Historical Comment (Keep & Transform) + +```typescript + +// Note: Database access has been migrated from direct IndexedDB calls to + PlatformServiceMixin +// This provides better platform abstraction and + consistent error handling across web/mobile/desktop +// When adding new database operations, use this.$getContact(), + this.$saveSettings(), etc. + +``` + +### Bad Historical Comment (Remove) + +```typescript + +// Old method getContactFromDB() removed - now handled by PlatformServiceMixin +// No need to call the old method anymore + +``` + +## 🎯 When to Use Each Pattern + +### Migration Notes + +- Use when functionality has moved to a different component/service + +- Explain the new location and why it's better + +- Provide guidance on how to use the new approach + +### Implementation Guides + +- Use when patterns have changed significantly + +- Explain the architectural benefits + +- Show how to implement the new pattern + +### Architectural Context + +- Use when the change represents a system-wide improvement + +- Explain the reasoning behind the change + +- Help future developers understand the evolution + +--- + +**See also**: `.cursor/rules/development/historical_comment_management.mdc` for + the core decision framework and best practices. + +## Model Implementation Checklist + +### Before Comment Review + +- [ ] **Code Analysis**: Review code for historical or outdated comments +- [ ] **Pattern Identification**: Identify comments that need transformation or removal +- [ ] **Context Understanding**: Understand the current state of the codebase +- [ ] **Transformation Planning**: Plan how to transform or remove comments + +### During Comment Transformation + +- [ ] **Pattern Selection**: Choose appropriate transformation pattern +- [ ] **Content Creation**: Create actionable guidance for future developers +- [ ] **Alternative Provision**: Suggest current best practices and approaches +- [ ] **Context Preservation**: Maintain enough information to understand changes + +### After Comment Transformation + +- [ ] **Code Review**: Ensure transformed comments provide actionable value +- [ ] **Pattern Documentation**: Document transformation patterns for team use +- [ ] **Team Communication**: Share comment transformation patterns with team +- [ ] **Process Integration**: Include comment patterns in code review checklists diff --git a/.cursor/rules/development/investigation_report_example.mdc b/.cursor/rules/development/investigation_report_example.mdc new file mode 100644 index 00000000..8014105c --- /dev/null +++ b/.cursor/rules/development/investigation_report_example.mdc @@ -0,0 +1,178 @@ +# Investigation Report Example + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Investigation methodology example + +## Investigation — Registration Dialog Test Flakiness + +## Objective + +Identify root cause of flaky tests related to registration dialogs in contact +import scenarios. + +## System Map + +- User action → ContactInputForm → ContactsView.addContact() → + + handleRegistrationPrompt() + +- setTimeout(1000ms) → Modal dialog → User response → Registration API call + +- Test execution → Wait for dialog → Assert dialog content → Click response + + button + +## Findings (Evidence) + +- **1-second timeout causes flakiness** — evidence: + + `src/views/ContactsView.vue:971-1000`; setTimeout(..., 1000) in + handleRegistrationPrompt() + +- **Import flow bypasses dialogs** — evidence: + + `src/views/ContactImportView.vue:500-520`; importContacts() calls + $insertContact() directly, no handleRegistrationPrompt() + +- **Dialog only appears in direct add flow** — evidence: + + `src/views/ContactsView.vue:774-800`; addContact() calls + handleRegistrationPrompt() after database insert + +## Hypotheses & Failure Modes + +- H1: 1-second timeout makes dialog appearance unpredictable; would fail when + + tests run faster than 1000ms + +- H2: Test environment timing differs from development; watch for CI vs local + + test differences + +## Corrections + +- Updated: "Multiple dialogs interfere with imports" → "Import flow never + + triggers dialogs - they only appear in direct contact addition" + +- Updated: "Complex batch registration needed" → "Simple timeout removal and + + test mode flag sufficient" + +## Diagnostics (Next Checks) + +- [ ] Repro on CI environment vs local + +- [ ] Measure actual dialog appearance timing + +- [ ] Test with setTimeout removed + +- [ ] Verify import flow doesn't call handleRegistrationPrompt + +## Risks & Scope + +- Impacted: Contact addition tests, registration workflow tests; Data: None; + + Users: Test suite reliability + +## Decision / Next Steps + +- Owner: Development Team; By: 2025-01-28 + +- Action: Remove 1-second timeout + add test mode flag; Exit criteria: Tests + + pass consistently + +## References + +- `src/views/ContactsView.vue:971-1000` + +- `src/views/ContactImportView.vue:500-520` + +- `src/views/ContactsView.vue:774-800` + +## Competence Hooks + +- Why this works: Code path tracing revealed separate execution flows, + + evidence disproved initial assumptions + +- Common pitfalls: Assuming related functionality without tracing execution + + paths, over-engineering solutions to imaginary problems + +- Next skill: Learn to trace code execution before proposing architectural + + changes + +- Teach-back: "What evidence shows that contact imports bypass registration + + dialogs?" + +## Key Learning Points + +### Evidence-First Approach + +This investigation demonstrates the importance of: + +1. **Tracing actual code execution** rather than making assumptions + +2. **Citing specific evidence** with file:line references + +3. **Validating problem scope** before proposing solutions + +4. **Considering simpler alternatives** before complex architectural changes + +### Code Path Tracing Value + +By tracing the execution paths, we discovered: + +- Import flow and direct add flow are completely separate + +- The "multiple dialog interference" problem didn't exist + +- A simple timeout removal would solve the actual issue + +### Prevention of Over-Engineering + +The investigation prevented: + +- Unnecessary database schema changes + +- Complex batch registration systems + +- Migration scripts for non-existent problems + +- Architectural changes based on assumptions + +--- + +**Status**: Active investigation methodology +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: software_development.mdc +**Stakeholders**: Development team, QA team + +## Model Implementation Checklist + +### Before Investigation + +- [ ] **Problem Definition**: Clearly define the problem to investigate +- [ ] **Scope Definition**: Determine investigation scope and boundaries +- [ ] **Methodology Planning**: Plan investigation approach and methods +- [ ] **Resource Assessment**: Identify required resources and tools + +### During Investigation + +- [ ] **Evidence Collection**: Gather relevant evidence and data systematically +- [ ] **Code Path Tracing**: Map execution flow for software investigations +- [ ] **Analysis**: Analyze evidence using appropriate methods +- [ ] **Documentation**: Document investigation process and findings + +### After Investigation + +- [ ] **Synthesis**: Synthesize findings into actionable insights +- [ ] **Report Creation**: Create comprehensive investigation report +- [ ] **Recommendations**: Provide clear, actionable recommendations +- [ ] **Team Communication**: Share findings and next steps with team diff --git a/.cursor/rules/development/logging_migration.mdc b/.cursor/rules/development/logging_migration.mdc new file mode 100644 index 00000000..bbb0c97d --- /dev/null +++ b/.cursor/rules/development/logging_migration.mdc @@ -0,0 +1,358 @@ +--- +alwaysApply: false +--- + +# Logging Migration — Patterns and Examples + +> **Agent role**: Reference this file for specific migration patterns and + examples when converting console.* calls to logger usage. + +## Migration — Auto‑Rewrites (Apply Every Time) + +### Exact Transforms + +- `console.debug(...)` → `logger.debug(...)` + +- `console.log(...)` → `logger.log(...)` (or `logger.info(...)` when + + clearly stateful) + +- `console.info(...)` → `logger.info(...)` + +- `console.warn(...)` → `logger.warn(...)` + +- `console.error(...)` → `logger.error(...)` + +### Multi-arg Handling + +- First arg becomes `message` (stringify safely if non-string). + +- Remaining args map 1:1 to `...args`: + + `console.info(msg, a, b)` → `logger.info(String(msg), a, b)` + +### Sole `Error` + +- `console.error(err)` → `logger.error(err.message, err)` + +### Object-wrapping Cleanup + +Replace `{{ userId, meta }}` wrappers with separate args: +`logger.info('User signed in', userId, meta)` + +## Level Guidelines with Examples + +### DEBUG Examples + +```typescript + +logger.debug('[HomeView] reloadFeedOnChange() called'); +logger.debug('[HomeView] Current filter settings', + settings.filterFeedByVisible, + settings.filterFeedByNearby, + settings.searchBoxes?.length ?? 0); +logger.debug('[FeedFilters] Toggling nearby filter', + this.isNearby, this.settingChanged, this.activeDid); + +``` + +**Avoid**: Vague messages (`'Processing data'`). + +### INFO Examples + +```typescript + +logger.info('[StartView] Component mounted', process.env.VITE_PLATFORM); +logger.info('[StartView] User selected new seed generation'); +logger.info('[SearchAreaView] Search box stored', + searchBox.name, searchBox.bbox); +logger.info('[ContactQRScanShowView] Contact registration OK', + contact.did); + +``` + +**Avoid**: Diagnostic details that belong in `debug`. + +### WARN Examples + +```typescript + +logger.warn('[ContactQRScanShowView] Invalid scan result – no value', + resultType); +logger.warn('[ContactQRScanShowView] Invalid QR format – no JWT in URL'); +logger.warn('[ContactQRScanShowView] JWT missing "own" field'); + +``` + +**Avoid**: Hard failures (those are `error`). + +### ERROR Examples + +```typescript + +logger.error('[HomeView Settings] initializeIdentity() failed', err); +logger.error('[StartView] Failed to load initialization data', error); +logger.error('[ContactQRScanShowView] Error processing contact QR', + error, rawValue); + +``` + +**Avoid**: Expected user cancels (use `info`/`debug`). + +## Context Hygiene Examples + +### Component Context + +```typescript + +const log = logger.withContext('UserService'); +log.info('User created', userId); +log.error('Failed to create user', error); + +``` + +If not using `withContext`, prefix message with `[ComponentName]`. + +### Emoji Guidelines + +Recommended set for visual scanning: + +- Start/finish: 🚀 / ✅ + +- Retry/loop: 🔄 + +- External call: 📡 + +- Data/metrics: 📊 + +- Inspection: 🔍 + +## Quick Before/After + +### **Before** + +```typescript + +console.log('User signed in', user.id, meta); +console.error('Failed to update profile', err); +console.info('Filter toggled', this.hasVisibleDid); + +``` + +### **After** + +```typescript + +import { logger } from '@/utils/logger'; + +logger.info('User signed in', user.id, meta); +logger.error('Failed to update profile', err); +logger.debug('[FeedFilters] Filter toggled', this.hasVisibleDid); + +``` + +## Checklist (for every PR) + +- [ ] No `console.*` (or properly pragma'd in the allowed locations) + +- [ ] Correct import path for `logger` + +- [ ] Rest-parameter call shape (`message, ...args`) + +- [ ] Right level chosen (debug/info/warn/error) + +- [ ] No secrets / oversized payloads / throwaway context objects + +- [ ] Component context provided (scoped logger or `[Component]` prefix) + +**See also**: + `.cursor/rules/development/logging_standards.mdc` for the core standards and rules. + +# Logging Migration — Patterns and Examples + +> **Agent role**: Reference this file for specific migration patterns and + examples when converting console.* calls to logger usage. + +## Migration — Auto‑Rewrites (Apply Every Time) + +### Exact Transforms + +- `console.debug(...)` → `logger.debug(...)` + +- `console.log(...)` → `logger.log(...)` (or `logger.info(...)` when + + clearly stateful) + +- `console.info(...)` → `logger.info(...)` + +- `console.warn(...)` → `logger.warn(...)` + +- `console.error(...)` → `logger.error(...)` + +### Multi-arg Handling + +- First arg becomes `message` (stringify safely if non-string). + +- Remaining args map 1:1 to `...args`: + + `console.info(msg, a, b)` → `logger.info(String(msg), a, b)` + +### Sole `Error` + +- `console.error(err)` → `logger.error(err.message, err)` + +### Object-wrapping Cleanup + +Replace `{{ userId, meta }}` wrappers with separate args: +`logger.info('User signed in', userId, meta)` + +## Level Guidelines with Examples + +### DEBUG Examples + +```typescript + +logger.debug('[HomeView] reloadFeedOnChange() called'); +logger.debug('[HomeView] Current filter settings', + settings.filterFeedByVisible, + settings.filterFeedByNearby, + settings.searchBoxes?.length ?? 0); +logger.debug('[FeedFilters] Toggling nearby filter', + this.isNearby, this.settingChanged, this.activeDid); + +``` + +**Avoid**: Vague messages (`'Processing data'`). + +### INFO Examples + +```typescript + +logger.info('[StartView] Component mounted', process.env.VITE_PLATFORM); +logger.info('[StartView] User selected new seed generation'); +logger.info('[SearchAreaView] Search box stored', + searchBox.name, searchBox.bbox); +logger.info('[ContactQRScanShowView] Contact registration OK', + contact.did); + +``` + +**Avoid**: Diagnostic details that belong in `debug`. + +### WARN Examples + +```typescript + +logger.warn('[ContactQRScanShowView] Invalid scan result – no value', + resultType); +logger.warn('[ContactQRScanShowView] Invalid QR format – no JWT in URL'); +logger.warn('[ContactQRScanShowView] JWT missing "own" field'); + +``` + +**Avoid**: Hard failures (those are `error`). + +### ERROR Examples + +```typescript + +logger.error('[HomeView Settings] initializeIdentity() failed', err); +logger.error('[StartView] Failed to load initialization data', error); +logger.error('[ContactQRScanShowView] Error processing contact QR', + error, rawValue); + +``` + +**Avoid**: Expected user cancels (use `info`/`debug`). + +## Context Hygiene Examples + +### Component Context + +```typescript + +const log = logger.withContext('UserService'); +log.info('User created', userId); +log.error('Failed to create user', error); + +``` + +If not using `withContext`, prefix message with `[ComponentName]`. + +### Emoji Guidelines + +Recommended set for visual scanning: + +- Start/finish: 🚀 / ✅ + +- Retry/loop: 🔄 + +- External call: 📡 + +- Data/metrics: 📊 + +- Inspection: 🔍 + +## Quick Before/After + +### **Before** + +```typescript + +console.log('User signed in', user.id, meta); +console.error('Failed to update profile', err); +console.info('Filter toggled', this.hasVisibleDid); + +``` + +### **After** + +```typescript + +import { logger } from '@/utils/logger'; + +logger.info('User signed in', user.id, meta); +logger.error('Failed to update profile', err); +logger.debug('[FeedFilters] Filter toggled', this.hasVisibleDid); + +``` + +## Checklist (for every PR) + +- [ ] No `console.*` (or properly pragma'd in the allowed locations) + +- [ ] Correct import path for `logger` + +- [ ] Rest-parameter call shape (`message, ...args`) + +- [ ] Right level chosen (debug/info/warn/error) + +- [ ] No secrets / oversized payloads / throwaway context objects + +- [ ] Component context provided (scoped logger or `[Component]` prefix) + +**See also**: + `.cursor/rules/development/logging_standards.mdc` for the core standards and rules. + +## Model Implementation Checklist + +### Before Logging Migration + +- [ ] **Code Review**: Identify all `console.*` calls in the codebase +- [ ] **Logger Import**: Verify logger utility is available and accessible +- [ ] **Context Planning**: Plan component context for each logging call +- [ ] **Level Assessment**: Determine appropriate log levels for each call + +### During Logging Migration + +- [ ] **Import Replacement**: Replace `console.*` with `logger.*` calls +- [ ] **Context Addition**: Add component context using scoped logger or prefix +- [ ] **Level Selection**: Choose appropriate log level (debug/info/warn/error) +- [ ] **Parameter Format**: Use rest-parameter call shape (`message, ...args`) + +### After Logging Migration + +- [ ] **Console Check**: Ensure no `console.*` methods remain (unless pragma'd) +- [ ] **Context Validation**: Verify all logging calls have proper context +- [ ] **Level Review**: Confirm log levels are appropriate for each situation +- [ ] **Testing**: Test logging functionality across all platforms diff --git a/.cursor/rules/development/logging_standards.mdc b/.cursor/rules/development/logging_standards.mdc new file mode 100644 index 00000000..5086567a --- /dev/null +++ b/.cursor/rules/development/logging_standards.mdc @@ -0,0 +1,176 @@ +# Agent Contract — TimeSafari Logging (Unified, MANDATORY) + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Mandatory logging standards + +## Overview + +This document defines unified logging standards for the TimeSafari project, +ensuring consistent, rest-parameter logging style using the project logger. +No `console.*` methods are allowed in production code. + +## Scope and Goals + +**Scope**: Applies to all diffs and generated code in this workspace unless +explicitly exempted below. + +**Goal**: One consistent, rest-parameter logging style using the project +logger; no `console.*` in production code. + +## Non‑Negotiables (DO THIS) + +- You **MUST** use the project logger; **DO NOT** use any `console.*` + + methods. + +- Import exactly as: + + - `import { logger } from '@/utils/logger'` + + - If `@` alias is unavailable, compute the correct relative path (do not + + fail). + +- Call signatures use **rest parameters**: `logger.info(message, ...args)` + +- Prefer primitives/IDs and small objects in `...args`; **never build a + + throwaway object** just to "wrap context". + +- Production defaults: Web = `warn+`, Electron = `error`, Dev/Capacitor = + + `info+` (override via `VITE_LOG_LEVEL`). + +- **Database persistence**: `info|warn|error` are persisted; `debug` is not. + + Use `logger.toDb(msg, level?)` for DB-only. + +## Available Logger API (Authoritative) + +- `logger.debug(message, ...args)` — verbose internals, timings, input/output + + shapes + +- `logger.log(message, ...args)` — synonym of `info` for general info + +- `logger.info(message, ...args)` — lifecycle, state changes, success paths + +- `logger.warn(message, ...args)` — recoverable issues, retries, degraded mode + +- `logger.error(message, ...args)` — failures, thrown exceptions, aborts + +- `logger.toDb(message, level?)` — DB-only entry (default level = `info`) + +- `logger.toConsoleAndDb(message, isError)` — console + DB (use sparingly) + +- `logger.withContext(componentName)` — returns a scoped logger + +## Level Guidelines (Use These Heuristics) + +### DEBUG + +Use for method entry/exit, computed values, filters, loops, retries, and +external call payload sizes. + +### INFO + +Use for user-visible lifecycle and completed operations. + +### WARN + +Use for recoverable issues, fallbacks, unexpected-but-handled conditions. + +### ERROR + +Use for unrecoverable failures, data integrity issues, and thrown +exceptions. + +## Context Hygiene (Consistent, Minimal, Helpful) + +- **Component context**: Prefer scoped logger. + +- **Emojis**: Optional and minimal for visual scanning. + +- **Sensitive data**: Never log secrets (tokens, keys, passwords) or + payloads >10KB. Prefer IDs over objects; redact/hash when needed. + +## DB Logging Rules + +- `debug` **never** persists automatically. + +- `info|warn|error` persist automatically. + +- For DB-only events (no console), call `logger.toDb('Message', + 'info'|'warn'|'error')`. + +## Exceptions (Tightly Scoped) + +Allowed paths (still prefer logger): + +- `**/*.test.*`, `**/*.spec.*` + +- `scripts/dev/**`, `scripts/migrate/**` + +To intentionally keep `console.*`, add a pragma on the previous line: + +```typescript + +// cursor:allow-console reason="short justification" +console.log('temporary output'); + +``` + +## CI & Diff Enforcement + +- Do not introduce `console.*` anywhere outside allowed, pragma'd spots. + +- If an import is missing, insert it and resolve alias/relative path + correctly. + +- Enforce rest-parameter call shape in reviews; replace object-wrapped + context. + +- Ensure environment log level rules remain intact (`VITE_LOG_LEVEL` + respected). + +--- + +**See also**: + `.cursor/rules/development/logging_migration.mdc` for migration patterns and examples. + +**Status**: Active and enforced +**Priority**: Critical +**Estimated Effort**: Ongoing reference +**Dependencies**: TimeSafari logger utility +**Stakeholders**: Development team, Code review team + +## Model Implementation Checklist + +### Before Adding Logging + +- [ ] **Logger Import**: Import logger as `import { logger } from + '@/utils/logger'` +- [ ] **Log Level Selection**: Determine appropriate log level + (debug/info/warn/error) +- [ ] **Context Planning**: Plan what context information to include +- [ ] **Sensitive Data Review**: Identify any sensitive data that needs redaction + +### During Logging Implementation + +- [ ] **Rest Parameters**: Use `logger.info(message, ...args)` format, not object + wrapping +- [ ] **Context Addition**: Include relevant IDs, primitives, or small objects in + args +- [ ] **Level Appropriateness**: Use correct log level for the situation +- [ ] **Scoped Logger**: Use `logger.withContext(componentName)` for + component-specific logging + +### After Logging Implementation + +- [ ] **Console Check**: Ensure no `console.*` methods are used (unless in + allowed paths) +- [ ] **Performance Review**: Verify logging doesn't impact performance +- [ ] **DB Persistence**: Use `logger.toDb()` for database-only logging if needed +- [ ] **Environment Compliance**: Respect `VITE_LOG_LEVEL` environment + variable diff --git a/.cursor/rules/development/planning_examples.mdc b/.cursor/rules/development/planning_examples.mdc new file mode 100644 index 00000000..310d73cd --- /dev/null +++ b/.cursor/rules/development/planning_examples.mdc @@ -0,0 +1,160 @@ +# Planning Examples — No Time Estimates + +> **Agent role**: Reference this file for detailed planning examples and + anti-patterns when creating project plans. + +## 🎯 Example Planning (No Time Estimates) + +### **Example 1: Simple Feature** + +``` + +Phase 1: Core implementation + +- Basic functionality + +- Single platform support + +- Unit tests + +Phase 2: Platform expansion + +- Multi-platform support + +- Integration tests + +Phase 3: Polish + +- User testing + +- Edge case handling + +``` + +### **Example 2: Complex Cross-Platform Feature** + +``` + +Phase 1: Foundation + +- Architecture design + +- Core service implementation + +- Basic web platform support + +Phase 2: Platform Integration + +- Mobile platform support + +- Desktop platform support + +- Cross-platform consistency + +Phase 3: Testing & Polish + +- Comprehensive testing + +- Error handling + +- User experience refinement + +``` + +## 🚫 Anti-Patterns to Avoid + +- **"This should take X days"** - Red flag for time estimation + +- **"Just a few hours"** - Ignores complexity and testing + +- **"Similar to X"** - Without considering differences + +- **"Quick fix"** - Nothing is ever quick in software + +- **"No testing needed"** - Testing always takes effort + +## ✅ Best Practices + +### **When Planning:** + +1. **Break down everything** - no work is too small to plan + +2. **Consider all platforms** - web, mobile, desktop differences + +3. **Include testing strategy** - unit, integration, and user testing + +4. **Account for unknowns** - there are always surprises + +5. **Focus on dependencies** - what blocks what + +### **When Presenting Plans:** + +1. **Show the phases** - explain the logical progression + +2. **Highlight dependencies** - what could block progress + +3. **Define milestones** - clear success criteria + +4. **Identify risks** - what could go wrong + +5. **Suggest alternatives** - ways to reduce scope or complexity + +## 🔄 Continuous Improvement + +### **Track Progress** + +- Record planned vs. actual phases completed + +- Identify what took longer than expected + +- Learn from complexity misjudgments + +- Adjust planning process based on experience + +### **Learn from Experience** + +- **Underestimated complexity**: Increase complexity categories + +- **Missed dependencies**: Improve dependency mapping + +- **Platform surprises**: Better platform research upfront + +## 🎯 Integration with Harbor Pilot + +This rule works in conjunction with: + +- **Project Planning**: Focuses on phases and milestones + +- **Resource Allocation**: Based on complexity, not time + +- **Risk Management**: Identifies blockers and dependencies + +- **Stakeholder Communication**: Sets progress-based expectations + +--- + +**See also**: `.cursor/rules/development/realistic_time_estimation.mdc` for + the core principles and framework. + +## Model Implementation Checklist + +### Before Planning + +- [ ] **Requirements Review**: Understand all requirements completely +- [ ] **Stakeholder Input**: Gather input from all stakeholders +- [ ] **Complexity Assessment**: Evaluate technical and business complexity +- [ ] **Platform Analysis**: Consider requirements across all target platforms + +### During Planning + +- [ ] **Phase Definition**: Define clear phases and milestones +- [ ] **Dependency Mapping**: Map dependencies between tasks +- [ ] **Risk Identification**: Identify potential risks and challenges +- [ ] **Testing Strategy**: Plan comprehensive testing approach + +### After Planning + +- [ ] **Stakeholder Review**: Review plan with stakeholders +- [ ] **Documentation**: Document plan clearly with phases and milestones +- [ ] **Team Communication**: Communicate plan to team +- [ ] **Progress Tracking**: Set up monitoring and tracking mechanisms diff --git a/.cursor/rules/development/realistic_time_estimation.mdc b/.cursor/rules/development/realistic_time_estimation.mdc new file mode 100644 index 00000000..f08a4398 --- /dev/null +++ b/.cursor/rules/development/realistic_time_estimation.mdc @@ -0,0 +1,128 @@ +# Realistic Time Estimation — Development Guidelines + +> **Agent role**: **DO NOT MAKE TIME ESTIMATES**. Instead, use phases, +> milestones, and complexity levels. Time estimates are consistently wrong +> and create unrealistic expectations. + +## Purpose + +Development time estimates are consistently wrong and create unrealistic +expectations. This rule ensures we focus on phases, milestones, and +complexity rather than trying to predict specific timeframes. + +## Critical Rule + +**NEVER provide specific time estimates** (hours, days, weeks) for +development tasks. Instead, use: + +- **Complexity levels** (Low, Medium, High, Critical) + +- **Phases and milestones** with clear acceptance criteria + +- **Platform-specific considerations** (Web, Mobile, Desktop) + +- **Testing requirements** and validation steps + +## Planning Framework + +### Complexity Assessment + +- **Low**: Simple changes, existing patterns, minimal testing + +- **Medium**: New features, moderate testing, some integration + +- **High**: Complex features, extensive testing, multiple platforms + +- **Critical**: Core architecture changes, full regression testing + +### Platform Categories + +- **Web**: Browser compatibility, responsive design, accessibility + +- **Mobile**: Native APIs, platform-specific testing, deployment + +- **Desktop**: Electron integration, system APIs, distribution + +### Testing Strategy + +- **Unit tests**: Core functionality validation + +- **Integration tests**: Component interaction testing + +- **E2E tests**: User workflow validation + +- **Platform tests**: Cross-platform compatibility + +## Process Guidelines + +### Planning Phase + +1. **Scope Definition**: Clear requirements and acceptance criteria + +2. **Complexity Assessment**: Evaluate technical and business complexity + +3. **Phase Breakdown**: Divide into logical, testable phases + +4. **Milestone Definition**: Define success criteria for each phase + +### Execution Phase + +1. **Phase 1**: Foundation and core implementation + +2. **Phase 2**: Feature completion and integration + +3. **Phase 3**: Testing, refinement, and documentation + +4. **Phase 4**: Deployment and validation + +### Validation Phase + +1. **Acceptance Testing**: Verify against defined criteria + +2. **Platform Testing**: Validate across target platforms + +3. **Performance Testing**: Ensure performance requirements met + +4. **Documentation**: Update relevant documentation + +## Remember + +**Your first estimate is wrong. Your second estimate is probably still +wrong. Focus on progress, not deadlines.** + +--- + +**See also**: + +- `.cursor/rules/development/planning_examples.mdc` for detailed planning examples + +- `.cursor/rules/development/complexity_assessment.mdc` for complexity evaluation + +**Status**: Active development guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: None +**Stakeholders**: Development team, Project managers + +## Model Implementation Checklist + +### Before Time Estimation + +- [ ] **Requirements Analysis**: Understand all requirements and acceptance criteria +- [ ] **Complexity Assessment**: Evaluate technical and business complexity +- [ ] **Platform Review**: Identify requirements across all target platforms +- [ ] **Stakeholder Input**: Gather input from all affected parties + +### During Time Estimation + +- [ ] **Phase Breakdown**: Divide work into logical, testable phases +- [ ] **Complexity Classification**: Assign complexity levels (Low/Medium/High/Critical) +- [ ] **Platform Considerations**: Account for platform-specific requirements +- [ ] **Testing Strategy**: Plan comprehensive testing approach + +### After Time Estimation + +- [ ] **Milestone Definition**: Define success criteria for each phase +- [ ] **Progress Tracking**: Set up monitoring and tracking mechanisms +- [ ] **Documentation**: Document estimation process and assumptions +- [ ] **Stakeholder Communication**: Share estimation approach and progress focus diff --git a/.cursor/rules/development/research_diagnostic.mdc b/.cursor/rules/development/research_diagnostic.mdc new file mode 100644 index 00000000..9d3c31aa --- /dev/null +++ b/.cursor/rules/development/research_diagnostic.mdc @@ -0,0 +1,262 @@ +--- +description: Use this workflow when doing **pre-implementation research, defect + investigations with uncertain repros, or clarifying system architecture and + behaviors**. +alwaysApply: false +--- + +```json + +{ + "coaching_level": "light", + "socratic_max_questions": 2, + "verbosity": "concise", + "timebox_minutes": null, + "format_enforcement": "strict" +} + +``` + +# Research & Diagnostic Workflow (R&D) + +## Purpose + +Provide a **repeatable, evidence-first** workflow to investigate features and +defects **before coding**. Outputs are concise reports, hypotheses, and next +steps—**not** code changes. + +## When to Use + +- Pre-implementation research for new features + +- Defect investigations (repros uncertain, user-specific failures) + +- Architecture/behavior clarifications (e.g., auth flows, merges, migrations) + +--- + +## Enhanced with Software Development Ruleset + +When investigating software issues, also apply: + +- **Code Path Tracing**: Required for technical investigations + +- **Evidence Validation**: Ensure claims are code-backed + +- **Solution Complexity Assessment**: Justify architectural changes + +--- + +## Output Contract (strict) + +1) **Objective** — 1–2 lines +2) **System Map (if helpful)** — short diagram or bullet flow (≤8 bullets) +3) **Findings (Evidence-linked)** — bullets; each with file/function refs +4) **Hypotheses & Failure Modes** — short list, each testable +5) **Corrections** — explicit deltas from earlier assumptions (if any) +6) **Diagnostics** — what to check next (logs, DB, env, repro steps) +7) **Risks & Scope** — what could break; affected components +8) **Decision/Next Steps** — what we'll do, who's involved, by when +9) **References** — code paths, ADRs, docs +10) **Competence & Collaboration Hooks** — brief, skimmable + +> Keep total length lean. Prefer links and bullets over prose. + +--- + +## Quickstart Template + +Copy/paste and fill: + +```md + +# Investigation — + +## Objective + + + +## System Map + +- + +- + +## Findings (Evidence) + +- — + + evidence: `src/path/file.ts:function` (lines X–Y); log snippet/trace id + +- — evidence: `...` + +## Hypotheses & Failure Modes + +- H1: ; would fail when + +- H2: ; watch for + +## Corrections + +- Updated: + +## Diagnostics (Next Checks) + +- [ ] Repro on + +- [ ] Inspect for + +- [ ] Capture + +## Risks & Scope + +- Impacted: ; Data: ; Users: + +## Decision / Next Steps + +- Owner: ; By: (YYYY-MM-DD) + +- Action: ; Exit criteria: + +## References + +- `src/...` + +- ADR: `docs/adr/xxxx-yy-zz-something.md` + +- Design: `docs/...` + +## Competence Hooks + +- Why this works: <≤3 bullets> + +- Common pitfalls: <≤3 bullets> + +- Next skill: <≤1 item> + +- Teach-back: "" + +``` + +--- + +## Evidence Quality Bar + +- **Cite the source** (file:func, line range if possible). + +- **Prefer primary evidence** (code, logs) over inference. + +- **Disambiguate platform** (Web/Capacitor/Electron) and **state** (migration, + + auth). + +- **Note uncertainty** explicitly. + +--- + +## Code Path Tracing (Required for Software Investigations) + +Before proposing solutions, trace the actual execution path: + +- [ ] **Entry Points**: + + Identify where the flow begins (user action, API call, etc.) + +- [ ] **Component Flow**: Map which components/methods are involved + +- [ ] **Data Path**: Track how data moves through the system + +- [ ] **Exit Points**: Confirm where the flow ends and what results + +- [ ] **Evidence Collection**: Gather specific code citations for each step + +--- + +## Collaboration Hooks + +- **Syncs:** 10–15m with QA/Security/Platform owners for high-risk areas. + +- **ADR:** Record major decisions; link here. + +- **Review:** Share repro + diagnostics checklist in PR/issue. + +--- + +## Integration with Other Rulesets + +### With software_development.mdc + +- **Enhanced Evidence Validation**: + + Use code path tracing for technical investigations + +- **Architecture Assessment**: + + Apply complexity justification to proposed solutions + +- **Impact Analysis**: Assess effects on existing systems before recommendations + +### With base_context.mdc + +- **Competence Building**: Focus on technical investigation skills + +- **Collaboration**: Structure outputs for team review and discussion + +--- + +## Self-Check (model, before responding) + +- [ ] Output matches the **Output Contract** sections. + +- [ ] Each claim has **evidence** or **uncertainty** is flagged. + +- [ ] Hypotheses are testable; diagnostics are actionable. + +- [ ] Competence + collaboration hooks present (≤120 words total). + +- [ ] Respect toggles; keep it concise. + +- [ ] **Code path traced** (for software investigations). + +- [ ] **Evidence validated** against actual code execution. + +--- + +## Optional Globs (examples) + +> Uncomment `globs` in the header if you want auto-attach behavior. + +- `src/platforms/**`, `src/services/**` — + + attach during service/feature investigations + +- `docs/adr/**` — attach when editing ADRs + +## Referenced Files + +- Consider including templates as context: `@adr_template.mdc`, + + `@investigation_report_example.mdc` + +## Model Implementation Checklist + +### Before Investigation + +- [ ] **Problem Definition**: Clearly define the research question or issue +- [ ] **Scope Definition**: Determine investigation scope and boundaries +- [ ] **Methodology Planning**: Plan investigation approach and methods +- [ ] **Resource Assessment**: Identify required resources and tools + +### During Investigation + +- [ ] **Evidence Collection**: Gather relevant evidence and data systematically +- [ ] **Code Path Tracing**: Map execution flow for software investigations +- [ ] **Analysis**: Analyze evidence using appropriate methods +- [ ] **Documentation**: Document investigation process and findings + +### After Investigation + +- [ ] **Synthesis**: Synthesize findings into actionable insights +- [ ] **Report Creation**: Create comprehensive investigation report +- [ ] **Recommendations**: Provide clear, actionable recommendations +- [ ] **Team Communication**: Share findings and next steps with team diff --git a/.cursor/rules/development/software_development.mdc b/.cursor/rules/development/software_development.mdc new file mode 100644 index 00000000..bbf3da4e --- /dev/null +++ b/.cursor/rules/development/software_development.mdc @@ -0,0 +1,227 @@ + +--- + +alwaysApply: false +--- + +# Software Development Ruleset + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Core development guidelines + +## Purpose + +Specialized guidelines for software development tasks including code review, +debugging, architecture decisions, and testing. + +## Core Principles + +### 1. Evidence-First Development + +- **Code Citations Required**: Always cite specific file:line references when + + making claims + +- **Execution Path Tracing**: Trace actual code execution before proposing + + architectural changes + +- **Assumption Validation**: Flag assumptions as "assumed" vs "evidence-based" + +### 2. Code Review Standards + +- **Trace Before Proposing**: Always trace execution paths before suggesting + + changes + +- **Evidence Over Inference**: Prefer code citations over logical deductions + +- **Scope Validation**: Confirm the actual scope of problems before proposing + + solutions + +### 3. Problem-Solution Validation + +- **Problem Scope**: Does the solution address the actual problem? + +- **Evidence Alignment**: Does the solution match the evidence? + +- **Complexity Justification**: Is added complexity justified by real needs? + +- **Alternative Analysis**: What simpler solutions were considered? + +### 4. Dependency Management & Environment Validation + +- **Pre-build Validation**: + + Always validate critical dependencies before executing + build scripts + +- **Environment Consistency**: Ensure team members have identical development + + environments + +- **Dependency Verification**: Check that required packages are installed and + + accessible + +- **Path Resolution**: Use `npx` for local dependencies to avoid PATH issues + +## Required Workflows + +### Before Proposing Changes + +- [ ] **Code Path Tracing**: Map execution flow from entry to exit + +- [ ] **Evidence Collection**: Gather specific code citations and logs + +- [ ] **Assumption Surfacing**: Identify what's proven vs. inferred + +- [ ] **Scope Validation**: Confirm the actual extent of the problem + +- [ ] **Dependency Validation**: Verify all required dependencies are available + + and accessible + +### During Solution Design + +- [ ] **Evidence Alignment**: Ensure solution addresses proven problems + +- [ ] **Complexity Assessment**: Justify any added complexity + +- [ ] **Alternative Evaluation**: Consider simpler approaches first + +- [ ] **Impact Analysis**: Assess effects on existing systems + +- [ ] **Environment Impact**: Assess how changes affect team member setups + +## Software-Specific Competence Hooks + +### Evidence Validation + +- **"What code path proves this claim?"** + +- **"How does data actually flow through the system?"** + +- **"What am I assuming vs. what can I prove?"** + +### Code Tracing + +- **"What's the execution path from user action to system response?"** + +- **"Which components actually interact in this scenario?"** + +- **"Where does the data originate and where does it end up?"** + +### Architecture Decisions + +- **"What evidence shows this change is necessary?"** + +- **"What simpler solution could achieve the same goal?"** + +- **"How does this change affect the existing system architecture?"** + +### Dependency & Environment Management + +- **"What dependencies does this feature require and are they properly + + declared?"** + +- **"How will this change affect team member development environments?"** + +- **"What validation can we add to catch dependency issues early?"** + +## Integration with Other Rulesets + +### With base_context.mdc + +- Inherits generic competence principles + +- Adds software-specific evidence requirements + +- Maintains collaboration and learning focus + +### With research_diagnostic.mdc + +- Enhances investigation with code path tracing + +- Adds evidence validation to diagnostic workflow + +- Strengthens problem identification accuracy + +## Usage Guidelines + +### When to Use This Ruleset + +- Code reviews and architectural decisions + +- Bug investigation and debugging + +- Performance optimization + +- Feature implementation planning + +- Testing strategy development + +### When to Combine with Others + +- **base_context + software_development**: General development tasks + +- **research_diagnostic + software_development**: Technical investigations + +- **All three**: Complex architectural decisions or major refactoring + +## Self-Check (model, before responding) + +- [ ] Code path traced and documented + +- [ ] Evidence cited with specific file:line references + +- [ ] Assumptions clearly flagged as proven vs. inferred + +- [ ] Solution complexity justified by evidence + +- [ ] Simpler alternatives considered and documented + +- [ ] Impact on existing systems assessed + +- [ ] Dependencies validated and accessible + +- [ ] Environment impact assessed for team members + +- [ ] Pre-build validation implemented where appropriate + +--- + +**See also**: `.cursor/rules/development/dependency_management.mdc` for + detailed dependency management practices. + +**Status**: Active development guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: base_context.mdc, research_diagnostic.mdc +**Stakeholders**: Development team, Code review team + +## Model Implementation Checklist + +### Before Development Work + +- [ ] **Code Path Tracing**: Map execution flow from entry to exit +- [ ] **Evidence Collection**: Gather specific code citations and logs +- [ ] **Assumption Surfacing**: Identify what's proven vs. inferred +- [ ] **Scope Validation**: Confirm the actual extent of the problem + +### During Development + +- [ ] **Evidence Alignment**: Ensure solution addresses proven problems +- [ ] **Complexity Assessment**: Justify any added complexity +- [ ] **Alternative Evaluation**: Consider simpler approaches first +- [ ] **Impact Analysis**: Assess effects on existing systems + +### After Development + +- [ ] **Code Path Validation**: Verify execution paths are correct +- [ ] **Evidence Documentation**: Document all code citations and evidence +- [ ] **Assumption Review**: Confirm all assumptions are documented +- [ ] **Environment Impact**: Assess how changes affect team member setups diff --git a/.cursor/rules/development/time.mdc b/.cursor/rules/development/time.mdc new file mode 100644 index 00000000..9aeb172b --- /dev/null +++ b/.cursor/rules/development/time.mdc @@ -0,0 +1,146 @@ +# Time Handling in Development Workflow + +**Author**: Matthew Raymer +**Date**: 2025-08-17 +**Status**: 🎯 **ACTIVE** - Production Ready + +## Overview + +This guide establishes **how time should be referenced and used** across the +development workflow. It is not tied to any one project, but applies to **all +feature development, issue investigations, ADRs, and documentation**. + +## General Principles + +- **Explicit over relative**: Always prefer absolute dates (`2025-08-17`) over + + relative references like "last week." + +- **ISO 8601 Standard**: Use `YYYY-MM-DD` format for all date references in + + docs, issues, ADRs, and commits. + +- **Time zones**: Default to **UTC** unless explicitly tied to user-facing + + behavior. + +- **Precision**: Only specify as much precision as needed (date vs. datetime vs. + + timestamp). + +- **Consistency**: Align time references across ADRs, commits, and investigation + + reports. + +## In Documentation & ADRs + +- Record decision dates using **absolute ISO dates**. + +- For ongoing timelines, state start and end explicitly (e.g., `2025-08-01` → + + `2025-08-17`). + +- Avoid ambiguous terms like *recently*, *last month*, or *soon*. + +- For time-based experiments (e.g., A/B tests), always include: + + - Start date + + - Expected duration + + - Review date checkpoint + +## In Code & Commits + +- Use **UTC timestamps** in logs, DB migrations, and serialized formats. + +- In commits, link changes to **date-bound ADRs or investigation docs**. + +- For migrations, include both **applied date** and **intended version window**. + +- Use constants for known fixed dates; avoid hardcoding arbitrary strings. + +## In Investigations & Research + +- Capture **when** an issue occurred (absolute time or version tag). + +- When describing failures: note whether they are **time-sensitive** (e.g., + + after + migrations, cache expirations). + +- Record diagnostic timelines in ISO format (not relative). + +- For performance regressions, annotate both **baseline timeframe** and + + **measurement timeframe**. + +## Collaboration Hooks + +- During reviews, verify **time references are clear, absolute, and + + standardized**. + +- In syncs, reframe relative terms ("this week") into shared absolute + + references. + +- Tag ADRs with both **date created** and **review by** checkpoints. + +## Self-Check Before Submitting + +- [ ] Did I check the time using the **developer's actual system time and + + timezone**? + +- [ ] Am I using absolute ISO dates? + +- [ ] Is UTC assumed unless specified otherwise? + +- [ ] Did I avoid ambiguous relative terms? + +- [ ] If duration matters, did I specify both start and end? + +- [ ] For future work, did I include a review/revisit date? + +--- + +**See also**: + +- `.cursor/rules/development/time_implementation.mdc` for + + detailed implementation instructions + +- `.cursor/rules/development/time_examples.mdc` for practical examples and patterns + +**Status**: Active time handling guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: None +**Stakeholders**: Development team, Documentation team + +**Maintainer**: Matthew Raymer +**Next Review**: 2025-09-17 + +## Model Implementation Checklist + +### Before Time-Related Work + +- [ ] **Time Context**: Understand what time information is needed +- [ ] **Format Review**: Review time formatting standards (UTC, ISO 8601) +- [ ] **Platform Check**: Identify platform-specific time requirements +- [ ] **User Context**: Consider user's timezone and preferences + +### During Time Implementation + +- [ ] **UTC Usage**: Use UTC for all system and log timestamps +- [ ] **Format Consistency**: Apply consistent time formatting patterns +- [ ] **Timezone Handling**: Properly handle timezone conversions +- [ ] **User Display**: Format times appropriately for user display + +### After Time Implementation + +- [ ] **Validation**: Verify time formats are correct and consistent +- [ ] **Testing**: Test time handling across different scenarios +- [ ] **Documentation**: Update relevant documentation with time patterns +- [ ] **Review**: Confirm implementation follows time standards diff --git a/.cursor/rules/development/time_examples.mdc b/.cursor/rules/development/time_examples.mdc new file mode 100644 index 00000000..f8a3d563 --- /dev/null +++ b/.cursor/rules/development/time_examples.mdc @@ -0,0 +1,243 @@ +# Time Examples — Practical Patterns + +> **Agent role**: Reference this file for practical examples and + patterns when working with time handling in development. + +## Examples + +### Good + +- "Feature flag rollout started on `2025-08-01` and will be reviewed on + + `2025-08-21`." + +- "Migration applied on `2025-07-15T14:00Z`." + +- "Issue reproduced on `2025-08-17T09:00-05:00 (local)` / + + `2025-08-17T14:00Z (UTC)`." + +### Bad + +- "Feature flag rolled out last week." + +- "Migration applied recently." + +- "Now is August, so we assume this was last month." + +### More Examples + +#### Issue Reports + +- ✅ **Good**: "User reported login failure at `2025-08-17T14:30:00Z`. Issue + + persisted until `2025-08-17T15:45:00Z`." + +- ❌ **Bad**: "User reported login failure earlier today. Issue lasted for a + + while." + +#### Release Planning + +- ✅ **Good**: "Feature X scheduled for release on `2025-08-25`. Testing + + window: `2025-08-20` to `2025-08-24`." + +- ❌ **Bad**: "Feature X will be released next week after testing." + +#### Performance Monitoring + +- ✅ **Good**: "Baseline performance measured on `2025-08-10T09:00:00Z`. + + Regression detected on `2025-08-15T14:00:00Z`." + +- ❌ **Bad**: "Performance was good last week but got worse this week." + +## Technical Implementation Examples + +### Database Storage + +```sql + +-- ✅ Good: Store in UTC +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, +updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + +-- ❌ Bad: Store in local time +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, +updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + +``` + +### API Responses + +```json + +// ✅ Good: Include both UTC and local time +{ + "eventTime": "2025-08-17T14:00:00Z", + "localTime": "2025-08-17T10:00:00-04:00", + "timezone": "America/New_York" +} + +// ❌ Bad: Only local time +{ + "eventTime": "2025-08-17T10:00:00-04:00" +} + +``` + +### Logging + +```python + +# ✅ Good: Log in UTC with timezone info + +logger.info(f"User action at {datetime.utcnow().isoformat()}Z (UTC)") + +# ❌ Bad: Log in local time + +logger.info(f"User action at {datetime.now()}") + +``` + +## Timezone Handling Examples + +### Good Timezone Usage + +```typescript + +// ✅ Good: Store UTC, convert for display +const eventTime = new Date().toISOString(); // Store in UTC +const localTime = new Date().toLocaleString('en-US', { + timeZone: 'America/New_York' +}); // Convert for display + +// ✅ Good: Include timezone context +const timestamp = { + utc: "2025-08-17T14:00:00Z", + local: "2025-08-17T10:00:00-04:00", + timezone: "America/New_York" +}; + +``` + +### Bad Timezone Usage + +```typescript + +// ❌ Bad: Assume timezone +const now = new Date(); // Assumes system timezone + +// ❌ Bad: Mix formats +const timestamp = "2025-08-17 10:00 AM"; // Ambiguous format + +``` + +## Common Patterns + +### Date Range References + +```typescript + +// ✅ Good: Explicit date ranges +const dateRange = { + start: "2025-08-01T00:00:00Z", + end: "2025-08-31T23:59:59Z" +}; + +// ❌ Bad: Relative ranges +const dateRange = { + start: "beginning of month", + end: "end of month" +}; + +``` + +### Duration References + +```typescript + +// ✅ Good: Specific durations +const duration = { + value: 30, + unit: "days", + startDate: "2025-08-01T00:00:00Z" +}; + +// ❌ Bad: Vague durations +const duration = "about a month"; + +``` + +### Version References + +```typescript + +// ✅ Good: Version with date +const version = { + number: "1.2.3", + releaseDate: "2025-08-17T10:00:00Z", + buildDate: "2025-08-17T09:30:00Z" +}; + +// ❌ Bad: Version without context +const version = "latest"; + +``` + +## References + +- [ISO 8601 Date and Time Standard](https://en.wikipedia.org/wiki/ISO_8601) + +- [IANA Timezone Database](https://www.iana.org/time-zones) + +- [ADR Template](./adr_template.md) + +- [Research & Diagnostic Workflow](./research_diagnostic.mdc) + +--- + +**Rule of Thumb**: Every time reference in development artifacts should be +**clear in 6 months without context**, and aligned to the **developer's actual +current time**. + +**Technical Rule of Thumb**: **Store in UTC, display in local time, always +include timezone context.** + +--- + +**See also**: + +- `.cursor/rules/development/time.mdc` for core principles + +- `.cursor/rules/development/time_implementation.mdc` for implementation instructions + +**Status**: Active examples and patterns +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: time.mdc, time_implementation.mdc +**Stakeholders**: Development team, Documentation team + +## Model Implementation Checklist + +### Before Time Implementation + +- [ ] **Time Context**: Understand what time information needs to be implemented +- [ ] **Format Review**: Review time formatting standards (UTC, ISO 8601) +- [ ] **Platform Check**: Identify platform-specific time requirements +- [ ] **User Context**: Consider user's timezone and display preferences + +### During Time Implementation + +- [ ] **UTC Storage**: Use UTC for all system and log timestamps +- [ ] **Format Consistency**: Apply consistent time formatting patterns +- [ ] **Timezone Handling**: Properly handle timezone conversions +- [ ] **User Display**: Format times appropriately for user display + +### After Time Implementation + +- [ ] **Format Validation**: Verify time formats are correct and consistent +- [ ] **Cross-Platform Testing**: Test time handling across different platforms +- [ ] **Documentation**: Update relevant documentation with time patterns +- [ ] **User Experience**: Confirm time display is clear and user-friendly diff --git a/.cursor/rules/development/time_implementation.mdc b/.cursor/rules/development/time_implementation.mdc new file mode 100644 index 00000000..f5c2da2a --- /dev/null +++ b/.cursor/rules/development/time_implementation.mdc @@ -0,0 +1,285 @@ +# Time Implementation — Technical Instructions + +> **Agent role**: Reference this file for detailed implementation instructions + when working with time handling in development. + +## Real-Time Context in Developer Interactions + +- The model must always resolve **"current time"** using the **developer's + + actual system time and timezone**. + +- When generating timestamps (e.g., in investigation logs, ADRs, or examples), + + the model should: + + - Use the **developer's current local time** by default. + + - Indicate the timezone explicitly (e.g., `2025-08-17T10:32-05:00`). + + - Optionally provide UTC alongside if context requires cross-team clarity. + +- When interpreting relative terms like *now*, *today*, *last week*: + + - Resolve them against the **developer's current time**. + + - Convert them into **absolute ISO-8601 values** in the output. + +## LLM Time Checking Instructions + +**CRITICAL**: The LLM must actively query the system for current time rather +than assuming or inventing times. + +### How to Check Current Time + +#### 1. **Query System Time (Required)** + +- **Always start** by querying the current system time using available tools + +- **Never assume** what the current time is + +- **Never use** placeholder values like "current time" or "now" + +#### 2. **Available Time Query Methods** + +- **System Clock**: Use `date` command or equivalent system time function + +- **Programming Language**: Use language-specific time functions (e.g., + + `Date.now()`, `datetime.now()`) + +- **Environment Variables**: Check for time-related environment variables + +- **API Calls**: Use time service APIs if available + +#### 3. **Required Time Information** + +When querying time, always obtain: + +- **Current Date**: YYYY-MM-DD format + +- **Current Time**: HH:MM:SS format (24-hour) + +- **Timezone**: Current system timezone or UTC offset + +- **UTC Equivalent**: Convert local time to UTC for cross-team clarity + +#### 4. **Time Query Examples** + +```bash + +# Example: Query system time + +$ date + +# Expected output: Mon Aug 17 10:32:45 EDT 2025 + +# Example: Query UTC time + +$ date -u + +# Expected output: Mon Aug 17 14:32:45 UTC 2025 + +``` + +```python + +# Example: Python time query + +import datetime +current_time = datetime.datetime.now() +utc_time = datetime.datetime.utcnow() +print(f"Local: {current_time}") +print(f"UTC: {utc_time}") + +``` + +```javascript + +// Example: JavaScript time query +const now = new Date(); +const utc = new Date().toISOString(); +console.log(`Local: ${now}`); +console.log(`UTC: ${utc}`); + +``` + +#### 5. **LLM Time Checking Workflow** + +1. **Query**: Actively query system for current time + +2. **Validate**: Confirm time data is reasonable and current + +3. **Format**: Convert to ISO 8601 format + +4. **Context**: Provide both local and UTC times when helpful + +5. **Document**: Show the source of time information + +#### 6. **Error Handling for Time Queries** + +- **If time query fails**: Ask user for current time or use "unknown time" + + with explanation + +- **If timezone unclear**: Default to UTC and ask for clarification + +- **If time seems wrong**: Verify with user before proceeding + +- **Always log**: Record when and how time was obtained + +#### 7. **Time Query Verification** + +Before using queried time, verify: + +- [ ] Time is recent (within last few minutes) + +- [ ] Timezone information is available + +- [ ] UTC conversion is accurate + +- [ ] Format follows ISO 8601 standard + +## Model Behavior Rules + +- **Never invent a "fake now"**: All "current time" references must come from + + the real system clock available at runtime. + +- **Check developer time zone**: If ambiguous, ask for clarification (e.g., + + "Should I use UTC or your local timezone?"). + +- **Format for clarity**: + + - Local time: `YYYY-MM-DDTHH:mm±hh:mm` + + - UTC equivalent (if needed): `YYYY-MM-DDTHH:mmZ` + +## Technical Implementation Notes + +### UTC Storage Principle + +- **Store all timestamps in UTC** in databases, logs, and serialized formats + +- **Convert to local time only for user display** + +- **Use ISO 8601 format** for all storage: `YYYY-MM-DDTHH:mm:ss.sssZ` + +### Common Implementation Patterns + +#### Database Storage + +```sql + +-- ✅ Good: Store in UTC +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, +updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + +``` + +#### API Responses + +```json + +// ✅ Good: Include both UTC and local time +{ + "eventTime": "2025-08-17T14:00:00Z", + "localTime": "2025-08-17T10:00:00-04:00", + "timezone": "America/New_York" +} + +``` + +#### Logging + +```python + +# ✅ Good: Log in UTC with timezone info + +logger.info(f"User action at {datetime.utcnow().isoformat()}Z (UTC)") + +``` + +### Timezone Handling Best Practices + +#### 1. Always Store Timezone Information + +- Include IANA timezone identifier (e.g., `America/New_York`) + +- Store UTC offset at time of creation + +- Handle daylight saving time transitions automatically + +#### 2. User Display Considerations + +- Convert UTC to user's preferred timezone + +- Show timezone abbreviation when helpful + +- Use relative time for recent events ("2 hours ago") + +#### 3. Edge Case Handling + +- **Daylight Saving Time**: Use timezone-aware libraries + +- **Leap Seconds**: Handle gracefully (rare but important) + +- **Invalid Times**: Validate before processing + +### Common Mistakes to Avoid + +#### 1. Timezone Confusion + +- ❌ **Don't**: Assume server timezone is user timezone + +- ✅ **Do**: Always convert UTC to user's local time for display + +#### 2. Format Inconsistency + +- ❌ **Don't**: Mix different time formats in the same system + +- ✅ **Do**: Standardize on ISO 8601 for all storage + +#### 3. Relative Time References + +- ❌ **Don't**: Use relative terms in persistent storage + +- ✅ **Do**: Convert relative terms to absolute timestamps immediately + +--- + +**See also**: + +- `.cursor/rules/development/time.mdc` for core principles + +- `.cursor/rules/development/time_examples.mdc` for practical examples + +**Status**: Active implementation guidelines +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: time.mdc +**Stakeholders**: Development team, DevOps team + +## Model Implementation Checklist + +### Before Time Implementation + +- [ ] **Time Context**: Understand what time information needs to be implemented +- [ ] **Format Review**: Review time formatting standards (UTC, ISO 8601) +- [ ] **Platform Check**: Identify platform-specific time requirements +- [ ] **User Context**: Consider user's timezone and display preferences + +### During Time Implementation + +- [ ] **UTC Storage**: Use UTC for all system and log timestamps +- [ ] **Format Consistency**: Apply consistent time formatting patterns +- [ ] **Timezone Handling**: Properly handle timezone conversions +- [ ] **User Display**: Format times appropriately for user display + +### After Time Implementation + +- [ ] **Format Validation**: Verify time formats are correct and consistent +- [ ] **Cross-Platform Testing**: Test time handling across different platforms +- [ ] **Documentation**: Update relevant documentation with time patterns +- [ ] **User Experience**: Confirm time display is clear and user-friendly diff --git a/.cursor/rules/development/type_safety_guide.mdc b/.cursor/rules/development/type_safety_guide.mdc new file mode 100644 index 00000000..4fa17ee8 --- /dev/null +++ b/.cursor/rules/development/type_safety_guide.mdc @@ -0,0 +1,212 @@ +--- +description: when dealing with types and Typesript +alwaysApply: false +--- + +```json + +{ + "coaching_level": "light", + "socratic_max_questions": 7, + "verbosity": "concise", + "timebox_minutes": null, + "format_enforcement": "strict" +} + +``` + +# TypeScript Type Safety Guidelines + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Type safety enforcement + +## Overview + +Practical rules to keep TypeScript strict and predictable. Minimize exceptions. + +## Core Rules + +1. **No `any`** + + - Use explicit types. If unknown, use `unknown` and **narrow** via guards. + +2. **Error handling uses guards** + + - Reuse guards from `src/interfaces/**` (e.g., `isDatabaseError`, + + `isApiError`). + + - Catch with `unknown`; never cast to `any`. + +3. **Dynamic property access is type‑safe** + + - Use `keyof` + `in` checks: + + ```ts + + obj[k as keyof typeof obj] + + ``` + + - Avoid `(obj as any)[k]`. + +## Type Safety Enforcement + +### Core Type Safety Rules + +- **No `any` Types**: Use explicit types or `unknown` with proper type guards + +- **Error Handling Uses Guards**: + + Implement and reuse type guards from `src/interfaces/**` + +- **Dynamic Property Access**: + + Use `keyof` + `in` checks for type-safe property access + +### Type Guard Patterns + +- **API Errors**: Use `isApiError(error)` guards for API error handling + +- **Database Errors**: + + Use `isDatabaseError(error)` guards for database operations + +- **Axios Errors**: + + Implement `isAxiosError(error)` guards for HTTP error handling + +### Implementation Guidelines + +- **Avoid Type Assertions**: + + Replace `as any` with proper type guards and interfaces + +- **Narrow Types Properly**: Use type guards to narrow `unknown` types safely + +- **Document Type Decisions**: Explain complex type structures and their purpose + +## Minimal Special Cases (document in PR when used) + +- **Vue refs / instances**: Use `ComponentPublicInstance` or specific + + component types for dynamic refs. + +- **3rd‑party libs without types**: Narrow immediately to a **known + + interface**; do not leave `any` hanging. + +## Patterns (short) + +### Database errors + +```ts + +try { await this.$addContact(contact); } +catch (e: unknown) { + if (isDatabaseError(e) && e.message.includes("Key already exists")) { + /* handle duplicate */ + } +} + +``` + +### API errors + +```ts + +try { await apiCall(); } +catch (e: unknown) { + if (isApiError(e)) { + const msg = e.response?.data?.error?.message; + } +} + +``` + +### Dynamic keys + +```ts + +const keys = Object.keys(newSettings).filter( +k => k in newSettings && newSettings[k as keyof typeof newSettings] !== + undefined +); + +``` + +## Checklists + +**Before commit** + +- [ ] No `any` (except documented, justified cases) + +- [ ] Errors handled via guards + +- [ ] Dynamic access uses `keyof`/`in` + +- [ ] Imports point to correct interfaces/types + +**Code review** + +- [ ] Hunt hidden `as any` + +- [ ] Guard‑based error paths verified + +- [ ] Dynamic ops are type‑safe + +- [ ] Prefer existing types over re‑inventing + +## Tools + +- `npm run lint-fix` — lint & auto‑fix + +- `npm run type-check` — strict type compilation (CI + pre‑release) + +- IDE: enable strict TS, ESLint/TS ESLint, Volar (Vue 3) + +## References + +- TS Handbook — + +- TS‑ESLint — + +- Vue 3 + TS — + +--- + +**Status**: Active type safety guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: TypeScript, ESLint, Vue 3 +**Stakeholders**: Development team + +- TS Handbook — + +- TS‑ESLint — + +- Vue 3 + TS — + +## Model Implementation Checklist + +### Before Type Implementation + +- [ ] **Type Analysis**: Understand current type definitions and usage +- [ ] **Interface Review**: Review existing interfaces and types +- [ ] **Error Handling**: Plan error handling with type guards +- [ ] **Dynamic Access**: Identify dynamic access patterns that need type safety + +### During Type Implementation + +- [ ] **Type Safety**: Ensure types provide meaningful safety guarantees +- [ ] **Error Guards**: Implement proper error handling with type guards +- [ ] **Dynamic Operations**: Use `keyof`/`in` for dynamic access +- [ ] **Import Validation**: Verify imports point to correct interfaces/types + +### After Type Implementation + +- [ ] **Linting Check**: Run `npm run lint-fix` to verify code quality +- [ ] **Type Check**: Run `npm run type-check` for strict type compilation +- [ ] **Code Review**: Hunt for hidden `as any` and type safety issues +- [ ] **Documentation**: Update type documentation and examples diff --git a/.cursor/rules/development_guide.mdc b/.cursor/rules/development_guide.mdc deleted file mode 100644 index cc43b000..00000000 --- a/.cursor/rules/development_guide.mdc +++ /dev/null @@ -1,10 +0,0 @@ ---- -description: rules used while developing -globs: -alwaysApply: true ---- -✅ use system date command to timestamp all interactions with accurate date and time -✅ python script files must always have a blank line at their end -✅ remove whitespace at the end of lines -✅ use npm run lint-fix to check for warnings -✅ do not use npm run dev let me handle running and supplying feedback diff --git a/.cursor/rules/docs/documentation.mdc b/.cursor/rules/docs/documentation.mdc new file mode 100644 index 00000000..e6e57bac --- /dev/null +++ b/.cursor/rules/docs/documentation.mdc @@ -0,0 +1,37 @@ +--- +alwaysApply: false +--- +# Directive for Documentation Generation + +1. Produce a **small, focused set of documents** rather than an overwhelming volume. +2. Ensure the content is **maintainable and worth preserving**, so that humans + are motivated to keep it up to date. +3. Prioritize **educational value**: the documents must clearly explain the + workings of the system. +4. Avoid **shallow, generic, or filler explanations** often found in AI-generated + documentation. +5. Aim for **clarity, depth, and usefulness**, so readers gain genuine understanding. +6. Always check the local system date to determine current date. + +## Model Implementation Checklist + +### Before Documentation Creation + +- [ ] **Scope Definition**: Define what needs to be documented +- [ ] **Audience Analysis**: Identify target readers and their needs +- [ ] **Content Planning**: Plan focused, educational content structure +- [ ] **Maintenance Planning**: Ensure content will be worth preserving + +### During Documentation Creation + +- [ ] **Educational Focus**: Clearly explain how the system works +- [ ] **Depth and Clarity**: Provide genuine understanding, not surface explanations +- [ ] **Focused Content**: Keep documents small and focused on specific topics +- [ ] **Current Date**: Check local system date for time-sensitive content + +### After Documentation Creation + +- [ ] **Quality Review**: Ensure content is clear, deep, and useful +- [ ] **Maintainability Check**: Verify content motivates humans to keep it updated +- [ ] **Audience Validation**: Confirm content meets target reader needs +- [ ] **Integration**: Integrate with existing documentation structure diff --git a/.cursor/rules/docs/markdown_core.mdc b/.cursor/rules/docs/markdown_core.mdc new file mode 100644 index 00000000..74024bff --- /dev/null +++ b/.cursor/rules/docs/markdown_core.mdc @@ -0,0 +1,210 @@ +# Markdown Core Standards & Automation + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Core markdown standards and automation + +## Overview + +This file combines core markdown formatting standards with automation +guidelines. AI agents must follow these rules DURING content generation, +not apply them after the fact. + +**Primary Focus**: Create educational content that increases human +competence, not just technical descriptions. + +## AI Generation Guidelines + +### **MANDATORY**: Follow These Rules While Writing + +When generating markdown content, you MUST: + +1. **Line Length**: Never exceed 80 characters per line +2. **Blank Lines**: Always add blank lines around headings, lists, and + code blocks +3. **Structure**: Use proper heading hierarchy and document templates +4. **Formatting**: Apply consistent formatting patterns immediately +5. **Educational Value**: Focus on increasing reader competence and + understanding + +### **DO NOT**: Generate content that violates these rules + +- ❌ Generate long lines that need breaking +- ❌ Create content without proper blank line spacing +- ❌ Use inconsistent formatting patterns +- ❌ Assume post-processing will fix violations +- ❌ Focus only on technical details without educational context +- ❌ Assume reader has extensive prior knowledge + +### **DO**: Generate compliant content from the start + +- ✅ Write within 80-character limits +- ✅ Add blank lines around all structural elements +- ✅ Use established templates and patterns +- ✅ Apply formatting standards immediately +- ✅ Explain concepts before implementation details +- ✅ Provide context and motivation for technical choices +- ✅ Include examples that illustrate key concepts + +## Core Formatting Standards + +### Line Length + +- **Maximum line length**: 80 characters +- **Exception**: Code blocks (JSON, shell, TypeScript, etc.) - no line + length enforcement +- **Rationale**: Ensures readability across different screen sizes and + terminal widths + +### Blank Lines + +- **Headings**: Must be surrounded by blank lines above and below +- **Lists**: Must be surrounded by blank lines above and below +- **Code blocks**: Must be surrounded by blank lines above and below +- **Maximum consecutive blank lines**: 1 (no multiple blank lines) +- **File start**: No blank lines at the beginning of the file +- **File end**: Single newline character at the end + +### Whitespace + +- **No trailing spaces**: Remove all trailing whitespace from lines +- **No tabs**: Use spaces for indentation +- **Consistent indentation**: 2 spaces for list items and nested content + +## Heading Standards + +### Format + +- **Style**: ATX-style headings (`#`, `##`, `###`, etc.) +- **Case**: Title case for general headings +- **Code references**: Use backticks for file names and technical terms + - ✅ `### Current package.json Scripts` + - ❌ `### Current Package.json Scripts` + +### Hierarchy + +- **H1 (#)**: Document title only +- **H2 (##)**: Major sections +- **H3 (###)**: Subsections +- **H4 (####)**: Sub-subsections +- **H5+**: Avoid deeper nesting + +## List Standards + +### Unordered Lists + +- **Marker**: Use `-` (hyphen) consistently +- **Indentation**: 2 spaces for nested items +- **Blank lines**: Surround lists with blank lines + +### Ordered Lists + +- **Format**: `1.`, `2.`, `3.` (sequential numbering) +- **Indentation**: 2 spaces for nested items +- **Blank lines**: Surround lists with blank lines + +### Task Lists + +- **Format**: `- [ ]` for incomplete, `- [x]` for complete +- **Indentation**: 2 spaces for nested items +- **Blank lines**: Surround lists with blank lines + +## Educational Content Standards + +### **Content Structure for Learning** + +- **Concept First**: Explain what something is before how to use it +- **Context Matters**: Explain why and when to use a feature +- **Progressive Disclosure**: Start simple, add complexity gradually +- **Real Examples**: Use concrete, relatable scenarios +- **Common Questions**: Anticipate and answer reader questions + +### **Writing for Understanding** + +- **Conversational Tone**: Write as if explaining to a colleague +- **Active Voice**: "You can do this" not "This can be done" +- **Question Format**: "What happens when..." to engage thinking +- **Analogies**: Use familiar concepts to explain complex ideas +- **Limitations**: Clearly state what solutions don't do + +## Code Block Standards + +### Inline Code + +- **Format**: Single backticks for inline code +- **Use cases**: File names, commands, variables, technical terms +- **Examples**: `package.json`, `npm run build`, `VITE_PLATFORM` + +### Code Blocks + +- **Format**: Triple backticks with language specification +- **Language**: Always specify the language for syntax highlighting +- **Blank lines**: Surround with blank lines above and below + +## Automation System + +### Available Commands + +- **`npm run markdown:fix`** - Fix formatting in all markdown files + using markdownlint-cli2 --fix +- **`npm run markdown:check`** - Validate formatting without fixing + using markdownlint-cli2 + +### How It Works + +1. **AI Agent Compliance** (Primary): AI follows markdown rules during + generation +2. **Pre-commit Hooks** (Backup): Catches any remaining formatting + issues +3. **GitHub Actions** (Pre-merge): Validates formatting before merge + +### Benefits + +- **No more manual fixes** - AI generates compliant content from start +- **Consistent style** - All files follow same standards +- **Faster development** - No need to fix formatting manually + +## Model Implementation Checklist + +### Before Generating Markdown Content + +- [ ] **Line Length**: Ensure no line exceeds 80 characters +- [ ] **Blank Lines**: Add blank lines around headings, lists, and code blocks +- [ ] **Whitespace**: Remove all trailing spaces, use 2-space indentation +- [ ] **Headings**: Use ATX-style with proper hierarchy (H1 for title only) +- [ ] **Lists**: Use consistent markers (- for unordered, 1. for ordered) +- [ ] **Code**: Specify language for fenced blocks, use backticks for inline +- [ ] **Educational Focus**: Plan content structure for learning progression +- [ ] **Audience Consideration**: Identify target reader knowledge level + +### After Generating Markdown Content + +- [ ] **Validation**: Run `npm run markdown:check` to verify compliance +- [ ] **Auto-fix**: Use `npm run markdown:fix` if any issues found +- [ ] **Review**: Confirm content follows established templates and patterns +- [ ] **Cross-reference**: Link to related documentation and templates +- [ ] **Educational Review**: Verify content increases reader competence +- [ ] **Example Validation**: Ensure examples illustrate key concepts clearly + +### Quality Assurance + +- [ ] **Readability**: Content is clear and follows project conventions +- [ ] **Consistency**: Formatting matches existing documentation style +- [ ] **Completeness**: All required sections and information included +- [ ] **Accuracy**: Technical details are correct and up-to-date +- [ ] **Educational Value**: Content increases reader understanding and competence +- [ ] **Context Clarity**: Reader understands when and why to use the information + +--- + +**See also**: + +- `.cursor/rules/meta_documentation.mdc` for comprehensive documentation workflow +- `.cursor/rules/docs/markdown_templates.mdc` for document templates +- `.cursor/rules/docs/markdown_workflow.mdc` for validation workflows + +**Status**: Active core standards and automation +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: None +**Stakeholders**: Documentation team, Development team diff --git a/.cursor/rules/docs/markdown_templates.mdc b/.cursor/rules/docs/markdown_templates.mdc new file mode 100644 index 00000000..eacb6720 --- /dev/null +++ b/.cursor/rules/docs/markdown_templates.mdc @@ -0,0 +1,314 @@ +# Markdown Templates & Examples + +> **Agent role**: Reference this file for document templates, structure, +> and examples when creating new documentation. +> +> **Focus**: Create educational content that increases human competence, +> not just technical descriptions. + +## Document Templates + +### Standard Document Template + +```markdown +# Document Title + +**Author**: Matthew Raymer +**Date**: YYYY-MM-DD +**Status**: 🎯 **STATUS** - Brief description + +## Overview + +Brief description of the document's purpose and scope. + +**Educational Goal**: What will the reader learn and how will it increase +their competence? + +## Current State + +Description of current situation or problem. + +**Why This Matters**: Explain the business value and user benefit of +addressing this situation. + +## Implementation Plan + +### Phase 1: Foundation + +- [ ] Task 1 +- [ ] Task 2 + +**Learning Context**: What concepts should the reader understand before +proceeding with implementation? + +## Next Steps + +1. **Review and approve plan** +2. **Begin implementation** +3. **Test and validate** + +**Continued Learning**: Where can the reader go next to deepen their +understanding? + +--- + +**Status**: Ready for implementation +**Priority**: Medium +**Estimated Effort**: X days +**Dependencies**: None +**Stakeholders**: Development team +``` + +### Technical Specification Template + +```markdown +# Technical Specification: [Feature Name] + +**Author**: Matthew Raymer +**Date**: YYYY-MM-DD +**Status**: 🎯 **DRAFT** - Under Review + +## Overview + +Brief description of the technical specification. + +**Business Context**: Why is this specification needed and what problem +does it solve for users? + +## Requirements + +### Functional Requirements + +- [ ] Requirement 1 +- [ ] Requirement 2 + +### Non-Functional Requirements + +- [ ] Performance requirement +- [ ] Security requirement + +## Technical Design + +### Architecture + +Description of the technical architecture. + +**Design Rationale**: Why was this architecture chosen over alternatives? +What are the trade-offs and benefits? + +### Data Models + +```typescript +interface ExampleModel { + id: string; + name: string; + createdAt: Date; +} +``` + +### API Design + +```typescript +interface APIResponse { + success: boolean; + data: T; + error?: string; +} +``` + +## Testing Strategy + +- [ ] Unit tests + +**Learning from Testing**: What insights does testing provide about the +system's behavior and design? + +--- + +## Educational Documentation Template + +### **Concept Explanation Template** + +```markdown +## What is [Concept Name]? + +Brief, clear definition of the concept. + +## Why Does [Concept Name] Matter? + +Explain the business value and user benefit. + +## How Does [Concept Name] Work? + +High-level explanation of the mechanism. + +## When Would You Use [Concept Name]? + +Real-world scenarios and use cases. + +## Common Misconceptions + +Address typical misunderstandings. + +## Examples + +Concrete examples that illustrate the concept. + +## Next Steps + +Where to learn more about related concepts. +``` + +### **Tutorial Template** + +```markdown +## Learning Objective + +What the reader will accomplish by the end. + +## Prerequisites + +What the reader should know before starting. + +## Step-by-Step Guide + +1. **Step 1**: What to do and why +2. **Step 2**: What to do and why +3. **Step 3**: What to do and why + +## Verification + +How to confirm the tutorial was successful. + +## Troubleshooting + +Common issues and how to resolve them. + +## What You've Learned + +Summary of key concepts and skills. + +## Next Steps + +Where to apply this knowledge next. +``` +- [ ] Integration tests +- [ ] E2E tests + +--- + +**Status**: Draft +**Priority**: High +**Estimated Effort**: X days +**Dependencies**: None +**Stakeholders**: Development team + +``` + +## Content Examples + +### JSON Examples + +```json +{ + "property": "value", + "nested": { + "property": "value" + } +} +``` + +### Shell Commands + +```bash +# Command with comment +npm run build:web + +# Multi-line command +VITE_GIT_HASH=`git log -1 --pretty=format:%h` \ + vite build --config vite.config.web.mts +``` + +### TypeScript Examples + +```typescript +// Function with JSDoc +const getEnvironmentConfig = (env: string) => { + switch (env) { + case 'prod': + return { /* production settings */ }; + default: + return { /* development settings */ }; + } +}; +``` + +## File Structure Standards + +### Document Header + +```markdown +# Document Title + +**Author**: Matthew Raymer +**Date**: YYYY-MM-DD +**Status**: 🎯 **STATUS** - Brief description + +## Overview + +Brief description of the document's purpose and scope. +``` + +### Section Organization + +Standard sections: Overview, Current State, Implementation Plan, +Technical Details, Testing & Validation, Next Steps + +## Common Patterns + +Standard implementation plans follow Phase 1 (Foundation), Phase 2 +(Features), Phase 3 (Testing & Polish) structure. + +## Model Implementation Checklist + +### Before Using Templates + +- [ ] **Template Selection**: Choose appropriate template for document type +- [ ] **Structure Review**: Understand required sections and organization +- [ ] **Content Planning**: Plan what information goes in each section +- [ ] **Audience Analysis**: Ensure template matches target audience needs + +### During Template Usage + +- [ ] **Section Completion**: Fill in all required sections completely +- [ ] **Example Integration**: Include relevant code examples and patterns +- [ ] **Formatting Consistency**: Apply markdown standards from core rules +- [ ] **Cross-references**: Link to related documentation and resources + +### After Template Usage + +- [ ] **Content Review**: Verify all sections contain appropriate content +- [ ] **Formatting Check**: Run `npm run markdown:check` for compliance +- [ ] **Template Validation**: Confirm document follows template structure +- [ ] **Quality Assessment**: Ensure content meets project standards + +### Template-Specific Requirements + +- [ ] **Standard Documents**: Include all required metadata and sections +- [ ] **Technical Specs**: Complete all requirement and design sections +- [ ] **Implementation Plans**: Define clear phases and milestones +- [ ] **Examples**: Provide relevant, working code examples + +--- + +**See also**: + +- `.cursor/rules/meta_documentation.mdc` for comprehensive documentation workflow +- `.cursor/rules/docs/markdown_core.mdc` for core formatting standards +- `.cursor/rules/docs/markdown_workflow.mdc` for validation workflows + +**Status**: Active templates and examples +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: markdown_core.mdc +**Stakeholders**: Documentation team, Development team diff --git a/.cursor/rules/docs/markdown_workflow.mdc b/.cursor/rules/docs/markdown_workflow.mdc new file mode 100644 index 00000000..0afadd2c --- /dev/null +++ b/.cursor/rules/docs/markdown_workflow.mdc @@ -0,0 +1,168 @@ +# Markdown Workflow & Validation + +> **Agent role**: Reference this file for markdown validation rules, +> enforcement procedures, and workflow management. + +## Markdownlint Configuration + +### Core Rules + +```json +{ + "MD013": { "line_length": 80, "code_blocks": false }, + "MD012": true, + "MD022": true, + "MD031": true, + "MD032": true, + "MD047": true, + "MD009": true, + "MD004": { "style": "dash" } +} +``` + +### Rule Explanations + +- **MD013**: Line length (80 chars, disabled for code blocks) +- **MD012**: No multiple consecutive blank lines +- **MD022**: Headings should be surrounded by blank lines +- **MD031**: Fenced code blocks should be surrounded by blank lines +- **MD032**: Lists should be surrounded by blank lines +- **MD047**: Files should end with a single newline +- **MD009**: No trailing spaces +- **MD004**: Consistent list markers (dash style) + +## Validation Commands + +### Check All MDC Files + +```bash +npm run markdown:check +``` + +### Auto-fix Formatting Issues + +```bash +npm run markdown:fix +``` + +### Check Single File + +```bash +npx markdownlint-cli2 .cursor/rules/filename.mdc +``` + +## Enforcement Workflow + +### Pre-commit Hooks + +- **Automatic**: `lint-staged` runs `markdownlint-cli2 --fix` on all + staged `.mdc` files +- **Result**: Files are automatically formatted before commit +- **Blocking**: Commits with unfixable violations are blocked + +### CI/CD Integration + +- **Build Pipeline**: Include markdownlint in automated builds +- **Quality Reports**: Generate documentation quality metrics +- **Build Failure**: Fail builds with critical violations + +### Team Guidelines + +- **PR Requirements**: All documentation PRs must pass markdownlint +- **Templates**: Use provided templates for new documents +- **Patterns**: Follow established patterns for consistency +- **Auto-fixing**: Let automation handle formatting, focus on content + +## Quality Assurance + +### Validation Checklist + +- [ ] All files pass `npm run markdown:check` +- [ ] Line length under 80 characters +- [ ] Proper blank line spacing around elements +- [ ] No trailing spaces +- [ ] Consistent list markers +- [ ] Proper heading hierarchy +- [ ] Code blocks have language specification + +### Common Issues & Fixes + +#### Trailing Spaces + +```bash +# Remove trailing spaces +sed -i 's/[[:space:]]*$//' .cursor/rules/**/*.mdc +``` + +#### Multiple Blank Lines + +```bash +# Remove multiple blank lines +sed -i '/^$/N;/^\n$/D' .cursor/rules/**/*.mdc +``` + +#### Missing Newlines + +```bash +# Add newline at end if missing +find .cursor/rules -name "*.mdc" -exec sed -i -e '$a\' {} \; +``` + +## Integration Points + +### Git Workflow + +1. **Edit**: Make changes to MDC files +2. **Stage**: `git add .cursor/rules/filename.mdc` +3. **Auto-fix**: `lint-staged` runs `markdownlint-cli2 --fix` +4. **Commit**: Changes are committed with perfect formatting + +### Development Workflow + +1. **Create/Edit**: Use templates from `markdown_templates.mdc` +2. **Validate**: Run `npm run markdown:check` before committing +3. **Auto-fix**: Use `npm run markdown:fix` for bulk fixes +4. **Review**: Ensure content quality, not just formatting + +## Model Implementation Checklist + +### Before Starting Workflow + +- [ ] **Configuration Review**: Understand markdownlint rules and settings +- [ ] **Tool Availability**: Ensure markdownlint-cli2 is installed and working +- [ ] **File Scope**: Identify which files need validation or fixing +- [ ] **Backup Strategy**: Consider backing up files before bulk operations + +### During Workflow Execution + +- [ ] **Validation First**: Run `npm run markdown:check` to identify issues +- [ ] **Issue Analysis**: Review and understand each validation error +- [ ] **Auto-fix Application**: Use `npm run markdown:fix` for automatic fixes +- [ ] **Manual Review**: Check files that couldn't be auto-fixed + +### After Workflow Completion + +- [ ] **Final Validation**: Confirm all files pass `npm run markdown:check` +- [ ] **Quality Review**: Verify formatting meets project standards +- [ ] **Documentation Update**: Update any related documentation or guides +- [ ] **Team Communication**: Share workflow results and any manual fixes needed + +### Workflow-Specific Requirements + +- [ ] **Pre-commit Hooks**: Ensure lint-staged configuration is working +- [ ] **CI/CD Integration**: Verify build pipeline includes markdown validation +- [ ] **Team Guidelines**: Confirm all team members understand the workflow +- [ ] **Error Resolution**: Document common issues and their solutions + +--- + +**See also**: + +- `.cursor/rules/docs/markdown_core.mdc` for core formatting standards +- `.cursor/rules/docs/markdown_templates.mdc` for document templates + +**Status**: Active workflow and validation +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: markdown_core.mdc, markdown_templates.mdc +**Stakeholders**: Development team, Documentation team diff --git a/.cursor/rules/documentation.mdc b/.cursor/rules/documentation.mdc deleted file mode 100644 index 1902ca73..00000000 --- a/.cursor/rules/documentation.mdc +++ /dev/null @@ -1,14 +0,0 @@ ---- -alwaysApply: true ---- -# Directive for Documentation Generation - -1. Produce a **small, focused set of documents** rather than an overwhelming volume. -2. Ensure the content is **maintainable and worth preserving**, so that humans -are motivated to keep it up to date. -3. Prioritize **educational value**: the documents must clearly explain the -workings of the system. -4. Avoid **shallow, generic, or filler explanations** often found in -AI-generated documentation. -5. Aim for **clarity, depth, and usefulness**, so readers gain genuine understanding. -6. Always check the local system date to determine current date. diff --git a/.cursor/rules/features/camera-implementation.mdc b/.cursor/rules/features/camera-implementation.mdc new file mode 100644 index 00000000..78c90e55 --- /dev/null +++ b/.cursor/rules/features/camera-implementation.mdc @@ -0,0 +1,163 @@ +# Camera Implementation Documentation + +## Overview + +This document describes how camera functionality is implemented across the +TimeSafari application. The application uses cameras for two main purposes: + +1. QR Code scanning + +2. Photo capture + +## Components + +### QRScannerDialog.vue + +Primary component for QR code scanning in web browsers. + +**Key Features:** + +- Uses `qrcode-stream` for web-based QR scanning + +- Supports both front and back cameras + +- Provides real-time camera status feedback + +- Implements error handling with user-friendly messages + +- Includes camera switching functionality + +**Camera Access Flow:** + +1. Checks for camera API availability + +2. Enumerates available video devices + +3. Requests camera permissions + +4. Initializes camera stream with preferred settings + +5. Handles various error conditions with specific messages + +### PhotoDialog.vue + +Component for photo capture and selection. + +**Key Features:** + +- Cross-platform photo capture interface + +- Image cropping capabilities + +- File selection fallback + +- Unified interface for different platforms + +## Services + +### QRScanner Services + +#### WebDialogQRScanner + +Web-based implementation of QR scanning. + +**Key Methods:** + +- `checkPermissions()`: Verifies camera permission status + +- `requestPermissions()`: Requests camera access + +- `isSupported()`: Checks for camera API support + +- Handles various error conditions with specific messages + +#### CapacitorQRScanner + +Native implementation using Capacitor's MLKit. + +**Key Features:** + +- Uses `@capacitor-mlkit/barcode-scanning` + +- Supports both front and back cameras + +- Implements permission management + +- Provides continuous scanning capability + +### Platform Services + +#### WebPlatformService + +Web-specific implementation of platform features. + +**Camera Capabilities:** + +- Uses HTML5 file input with capture attribute + +- Falls back to file selection if camera unavailable + +- Processes captured images for consistent format + +#### CapacitorPlatformService + +Native implementation using Capacitor. + +**Camera Features:** + +- Uses `Camera.getPhoto()` for native camera access + +- Supports image editing + +- Configures high-quality image capture + +- Handles base64 image processing + +#### ElectronPlatformService + +Desktop implementation (currently unimplemented). + +--- + +**See also**: + +- `.cursor/rules/features/camera_technical.mdc` for + + detailed technical implementation + +- `.cursor/rules/features/camera_platforms.mdc` for platform-specific details + +**Status**: Active camera implementation overview +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: None +**Stakeholders**: Development team, Camera feature team + +- iOS and Android devices + +- Desktop platforms + +- Various network conditions + +## Model Implementation Checklist + +### Before Camera Implementation + +- [ ] **Platform Analysis**: Understand camera requirements across all platforms +- [ ] **Feature Planning**: Plan QR scanning and photo capture features +- [ ] **Service Planning**: Plan camera service architecture +- [ ] **Testing Strategy**: Plan testing across web, mobile, and desktop + +### During Camera Implementation + +- [ ] **Component Development**: Implement QRScannerDialog and PhotoDialog +- [ ] **Service Implementation**: Implement platform-specific camera services +- [ ] **Permission Handling**: Implement proper camera permission management +- [ ] **Error Handling**: Implement graceful error handling for camera failures + +### After Camera Implementation + +- [ ] **Cross-Platform Testing**: Test camera functionality across all platforms +- [ ] **Feature Validation**: Verify QR scanning and photo capture work correctly +- [ ] **Performance Testing**: Ensure camera performance meets requirements +- [ ] **Documentation Update**: Update camera implementation documentation diff --git a/.cursor/rules/features/camera_platforms.mdc b/.cursor/rules/features/camera_platforms.mdc new file mode 100644 index 00000000..a7a35664 --- /dev/null +++ b/.cursor/rules/features/camera_platforms.mdc @@ -0,0 +1,225 @@ +# Camera Platform-Specific Implementation + +> **Agent role**: + Reference this file for platform-specific camera implementation details. + +## Web Platform + +### Implementation Details + +- Uses `getUserMedia` API for camera access + +- Implements fallback to file input if camera unavailable + +- Handles browser compatibility issues + +- Requires HTTPS for camera access + +### Browser Support + +- Chrome: Full support + +- Firefox: Full support + +- Safari: Limited support + +- Edge: Full support + +### Fallback Mechanisms + +1. Camera access via getUserMedia + +2. File input for image upload + +3. Drag and drop support + +4. Clipboard paste support + +## Mobile Platform (Capacitor) + +### iOS Implementation + +- Uses `@capacitor-mlkit/barcode-scanning` + +- Implements proper permission handling + +- Supports both front and back cameras + +- Handles camera switching + +### Android Implementation + +- Uses `@capacitor-mlkit/barcode-scanning` + +- Implements proper permission handling + +- Supports both front and back cameras + +- Handles camera switching + +### Permission Handling + +- Camera permissions requested at runtime + +- Permission state tracked and cached + +- Graceful handling of denied permissions + +- Clear user guidance for enabling permissions + +## Desktop Platform (Electron) + +### Current Status + +- Camera implementation pending + +- Will use platform-specific APIs + +- Requires proper permission handling + +- Will support both built-in and external cameras + +### Planned Implementation + +- Native camera access via Electron + +- Platform-specific camera APIs + +- Proper permission handling + +- Camera switching support + +## Platform Detection + +### Implementation + +- Uses `PlatformServiceFactory` for platform detection + +- Implements platform-specific camera services + +- Handles platform-specific error conditions + +- Provides platform-specific user guidance + +### Service Selection + +- Web: `WebPlatformService` + +- Mobile: `CapacitorPlatformService` + +- Desktop: `ElectronPlatformService` + +## Cross-Platform Compatibility + +### Common Interface + +- Unified camera service interface + +- Platform-specific implementations + +- Consistent error handling + +- Unified user experience + +### Feature Parity + +- Core camera functionality across platforms + +- Platform-specific optimizations + +- Consistent user interface + +- Unified error messages + +## Platform-Specific Features + +### Web + +- Browser-based camera access + +- File upload fallback + +- Drag and drop support + +- Clipboard paste support + +### Mobile + +- Native camera access + +- Barcode scanning + +- Photo capture + +- Camera switching + +### Desktop + +- Native camera access (planned) + +- External camera support (planned) + +- Advanced camera controls (planned) + +## Testing Strategy + +### Platform Coverage + +- Web: Multiple browsers + +- Mobile: iOS and Android devices + +- Desktop: Windows, macOS, Linux + +### Test Scenarios + +- Permission handling + +- Camera access + +- Error conditions + +- Platform compatibility + +- Performance metrics + +--- + +**See also**: + +- `.cursor/rules/features/camera-implementation.mdc` for + + core implementation overview + +- `.cursor/rules/features/camera_technical.mdc` for + + technical implementation details + +**Status**: Active platform-specific implementation guide +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: camera-implementation.mdc +**Stakeholders**: Development team, Platform team + +## Model Implementation Checklist + +### Before Camera Platform Implementation + +- [ ] **Platform Analysis**: Identify target platforms and their camera capabilities +- [ ] **Feature Planning**: Plan platform-specific camera features +- [ ] **Integration Planning**: Plan integration with existing camera systems +- [ ] **Testing Strategy**: Plan testing across all target platforms + +### During Camera Platform Implementation + +- [ ] **Platform Services**: Implement platform-specific camera functionality +- [ ] **Feature Development**: Implement planned camera features for each platform +- [ ] **Integration**: Integrate with existing camera infrastructure +- [ ] **Performance Optimization**: Optimize camera performance for each platform + +### After Camera Platform Implementation + +- [ ] **Cross-Platform Testing**: Test camera functionality across all platforms +- [ ] **Feature Validation**: Verify all planned features work correctly +- [ ] **Performance Testing**: Ensure camera performance meets requirements +- [ ] **Documentation Update**: Update platform-specific camera documentation diff --git a/.cursor/rules/features/camera_technical.mdc b/.cursor/rules/features/camera_technical.mdc new file mode 100644 index 00000000..4f69a3de --- /dev/null +++ b/.cursor/rules/features/camera_technical.mdc @@ -0,0 +1,203 @@ +# Camera Technical Implementation — Details and Best Practices + +> **Agent role**: Reference this file for + detailed technical implementation when working with camera features. + +## Platform-Specific Considerations + +### iOS + +- Requires `NSCameraUsageDescription` in Info.plist + +- Supports both front and back cameras + +- Implements proper permission handling + +### Android + +- Requires camera permissions in manifest + +- Supports both front and back cameras + +- Handles permission requests through Capacitor + +### Web + +- Requires HTTPS for camera access + +- Implements fallback mechanisms + +- Handles browser compatibility issues + +## Error Handling + +### Common Error Scenarios + +1. No camera found + +2. Permission denied + +3. Camera in use by another application + +4. HTTPS required + +5. Browser compatibility issues + +### Error Response + +- User-friendly error messages + +- Troubleshooting tips + +- Clear instructions for resolution + +- Platform-specific guidance + +## Security Considerations + +### Permission Management + +- Explicit permission requests + +- Permission state tracking + +- Graceful handling of denied permissions + +### Data Handling + +- Secure image processing + +- Proper cleanup of camera resources + +- No persistent storage of camera data + +## Best Practices + +### Camera Access + +1. Always check for camera availability + +2. Request permissions explicitly + +3. Handle all error conditions + +4. Provide clear user feedback + +5. Implement proper cleanup + +### Performance + +1. Optimize camera resolution + +2. Implement proper resource cleanup + +3. Handle camera switching efficiently + +4. Manage memory usage + +### User Experience + +1. Clear status indicators + +2. Intuitive camera controls + +3. Helpful error messages + +4. Smooth camera switching + +5. Responsive UI feedback + +## Future Improvements + +### Planned Enhancements + +1. Implement Electron camera support + +2. Add advanced camera features + +3. Improve error handling + +4. Enhance user feedback + +5. Optimize performance + +### Known Issues + +1. Electron camera implementation pending + +2. Some browser compatibility limitations + +3. Platform-specific quirks to address + +## Dependencies + +### Key Packages + +- `@capacitor-mlkit/barcode-scanning` + +- `qrcode-stream` + +- `vue-picture-cropper` + +- Platform-specific camera APIs + +## Testing + +### Test Scenarios + +1. Permission handling + +2. Camera switching + +3. Error conditions + +4. Platform compatibility + +5. Performance metrics + +### Test Environment + +- Multiple browsers + +- iOS and Android devices + +- Desktop platforms + +--- + +**See also**: + +- `.cursor/rules/features/camera-implementation.mdc` for + + core implementation overview + +- `.cursor/rules/features/camera_platforms.mdc` for platform-specific details + +**Status**: Active technical implementation guide +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: camera-implementation.mdc +**Stakeholders**: Development team, Camera feature team + +## Model Implementation Checklist + +### Before Camera Implementation + +- [ ] **Platform Analysis**: Identify target platforms and camera capabilities +- [ ] **Permission Planning**: Plan permission handling for camera access +- [ ] **Dependency Review**: Review required camera packages and APIs +- [ ] **Testing Strategy**: Plan testing across multiple platforms + +### During Camera Implementation + +- [ ] **Platform Services**: Implement platform-specific camera services +- [ ] **Permission Handling**: Implement proper camera permission handling +- [ ] **Error Handling**: Implement graceful error handling for camera failures +- [ ] **Performance Optimization**: Optimize camera performance and responsiveness + +### After Camera Implementation + +- [ ] **Cross-Platform Testing**: Test camera functionality across all platforms +- [ ] **Permission Testing**: Test permission handling and user feedback +- [ ] **Performance Validation**: Verify camera performance meets requirements +- [ ] **Documentation Update**: Update camera technical documentation diff --git a/.cursor/rules/harbor_pilot_universal.mdc b/.cursor/rules/harbor_pilot_universal.mdc new file mode 100644 index 00000000..91d099f7 --- /dev/null +++ b/.cursor/rules/harbor_pilot_universal.mdc @@ -0,0 +1,207 @@ +--- +alwaysApply: true +inherits: base_context.mdc +--- +```json +{ + "coaching_level": "standard", + "socratic_max_questions": 2, + "verbosity": "concise", + "timebox_minutes": 10, + "format_enforcement": "strict" +} +``` + +# Harbor Pilot — Universal Directive for Human-Facing Technical Guides + +**Author**: System/Shared +**Date**: 2025-08-21 (UTC) +**Status**: 🚢 ACTIVE — General ruleset extending *Base Context — Human Competence First* + +> **Alignment with Base Context** +> - **Purpose fit**: Prioritizes human competence and collaboration while delivering reproducible artifacts. +> - **Output Contract**: This directive **adds universal constraints** for any technical topic while **inheriting** the Base Context contract sections. +> - **Toggles honored**: Uses the same toggle semantics; defaults above can be overridden by the caller. + +--- + +## Objective +Produce a **developer-grade, reproducible guide** for any technical topic that onboards a competent practitioner **without meta narration** and **with evidence-backed steps**. + +## Scope & Constraints +- **One Markdown document** as the deliverable. +- Use **absolute dates** in **UTC** (e.g., `2025-08-21T14:22Z`) — avoid “today/yesterday”. +- Include at least **one diagram** (Mermaid preferred). Choose the most fitting type: + - `sequenceDiagram` (protocols/flows), `flowchart`, `stateDiagram`, `gantt` (timelines), or `classDiagram` (schemas). +- Provide runnable examples where applicable: + - **APIs**: `curl` + one client library (e.g., `httpx` for Python). + - **CLIs**: literal command blocks and expected output snippets. + - **Code**: minimal, self-contained samples (language appropriate). +- Cite **evidence** for *Works/Doesn’t* items (timestamps, filenames, line numbers, IDs/status codes, or logs). +- If something is unknown, output `TODO:` — **never invent**. + +## Required Sections (extends Base Output Contract) +Follow this exact order **after** the Base Contract’s **Objective → Result → Use/Run** headers: + +1. **Context & Scope** + - Problem statement, audience, in/out-of-scope bullets. +2. **Artifacts & Links** + - Repos/PRs, design docs, datasets/HARs/pcaps, scripts/tools, dashboards. +3. **Environment & Preconditions** + - OS/runtime, versions/build IDs, services/endpoints/URLs, credentials/auth mode (describe acquisition, do not expose secrets). +4. **Architecture / Process Overview** + - Short prose + **one diagram** selected from the list above. +5. **Interfaces & Contracts (choose one)** + - **API-based**: Endpoint table (*Step, Method, Path/URL, Auth, Key Headers/Params, Sample Req/Resp ref*). + - **Data/Files**: I/O contract table (*Source, Format, Schema/Columns, Size, Validation rules*). + - **Systems/Hardware**: Interfaces table (*Port/Bus, Protocol, Voltage/Timing, Constraints*). +6. **Repro: End-to-End Procedure** + - Minimal copy-paste steps with code/commands and **expected outputs**. +7. **What Works (with Evidence)** + - Each item: **Time (UTC)** • **Artifact/Req IDs** • **Status/Result** • **Where to verify**. +8. **What Doesn’t (Evidence & Hypotheses)** + - Each failure: locus (file/endpoint/module), evidence snippet; short hypothesis and **next probe**. +9. **Risks, Limits, Assumptions** + - SLOs/limits, rate/size caps, security boundaries (CORS/CSRF/ACLs), retries/backoff/idempotency patterns. +10. **Next Steps (Owner • Exit Criteria • Target Date)** + - Actionable, assigned, and time-bound. +11. **References** + - Canonical docs, specs, tickets, prior analyses. + +> **Competence Hooks (per Base Context; keep lightweight):** +> - *Why this works* (≤3 bullets) — core invariants or guarantees. +> - *Common pitfalls* (≤3 bullets) — the traps we saw in evidence. +> - *Next skill unlock* (1 line) — the next capability to implement/learn. +> - *Teach-back* (1 line) — prompt the reader to restate the flow/architecture. + +> **Collaboration Hooks (per Base Context):** +> - Name reviewers for **Interfaces & Contracts** and the **diagram**. +> - Short **sign-off checklist** before merging/publishing the guide. + +## Do / Don’t (Base-aligned) +- **Do** quantify progress only against a defined scope with acceptance criteria. +- **Do** include minimal sample payloads/headers or I/O schemas; redact sensitive values. +- **Do** keep commentary lean; if timeboxed, move depth to **Deferred for depth**. +- **Don’t** use marketing language or meta narration (“Perfect!”, “tool called”, “new chat”). +- **Don’t** include IDE-specific chatter or internal rules unrelated to the task. + +## Validation Checklist (self-check before returning) +- [ ] All Required Sections present and ordered. +- [ ] Diagram compiles (basic Mermaid syntax) and fits the problem. +- [ ] If API-based, **Auth** and **Key Headers/Params** are listed for each endpoint. +- [ ] Repro section includes commands/code **and expected outputs**. +- [ ] Every Works/Doesn’t item has **UTC timestamp**, **status/result**, and **verifiable evidence**. +- [ ] Next Steps include **Owner**, **Exit Criteria**, **Target Date**. +- [ ] Unknowns are `TODO:` — no fabrication. +- [ ] Base **Output Contract** sections satisfied (Objective/Result/Use/Run/Competence/Collaboration/Assumptions/References). + +## Universal Template (fill-in) +```markdown +# — Working Notes (As of YYYY-MM-DDTHH:MMZ) + +## Objective +<one line> + +## Result +<link to the produced guide file or say “this document”> + +## Use/Run +<how to apply/test and where to run samples> + +## Context & Scope +- Audience: <role(s)> +- In scope: <bullets> +- Out of scope: <bullets> + +## Artifacts & Links +- Repo/PR: <link> +- Data/Logs: <paths or links> +- Scripts/Tools: <paths> +- Dashboards: <links> + +## Environment & Preconditions +- OS/Runtime: <details> +- Versions/Builds: <list> +- Services/Endpoints: <list> +- Auth mode: <Bearer/Session/Keys + how acquired> + +## Architecture / Process Overview +<short prose> +```mermaid +<one suitable diagram: sequenceDiagram | flowchart | stateDiagram | gantt | classDiagram> +``` + +## Interfaces & Contracts +### If API-based +| Step | Method | Path/URL | Auth | Key Headers/Params | Sample | +|---|---|---|---|---|---| +| <…> | <…> | <…> | <…> | <…> | below | + +### If Data/Files +| Source | Format | Schema/Columns | Size | Validation | +|---|---|---|---|---| +| <…> | <…> | <…> | <…> | <…> | + +### If Systems/Hardware +| Interface | Protocol | Timing/Voltage | Constraints | Notes | +|---|---|---|---|---| +| <…> | <…> | <…> | <…> | <…> | + +## Repro: End-to-End Procedure +```bash +# commands / curl examples (redacted where necessary) +``` +```python +# minimal client library example (language appropriate) +``` +> Expected output: <snippet/checks> + +## What Works (Evidence) +- ✅ <short statement> + - **Time**: <YYYY-MM-DDTHH:MMZ> + - **Evidence**: file/line/log or request id/status + - **Verify at**: <where> + +## What Doesn’t (Evidence & Hypotheses) +- ❌ <short failure> at `<component/endpoint/file>` + - **Time**: <YYYY-MM-DDTHH:MMZ> + - **Evidence**: <snippet/id/status> + - **Hypothesis**: <short> + - **Next probe**: <short> + +## Risks, Limits, Assumptions +<bullets: limits, security boundaries, retries/backoff, idempotency, SLOs> + +## Next Steps +| Owner | Task | Exit Criteria | Target Date (UTC) | +|---|---|---|---| +| <name> | <action> | <measurable outcome> | <YYYY-MM-DD> | + +## References +<links/titles> + +## Competence Hooks +- *Why this works*: <≤3 bullets> +- *Common pitfalls*: <≤3 bullets> +- *Next skill unlock*: <1 line> +- *Teach-back*: <1 line> + +## Collaboration Hooks +- Reviewers: <names/roles> +- Sign-off checklist: <≤5 checks> + +## Assumptions & Limits +<bullets> + +## Deferred for depth +<park deeper material here to respect timeboxing> +``` + +--- + +**Notes for Implementers:** +- Respect Base *Do-Not* (no filler, no invented facts, no censorship). +- Prefer clarity over completeness when timeboxed; capture unknowns explicitly. +- Apply historical comment management rules (see `.cursor/rules/historical_comment_management.mdc`) +- Apply realistic time estimation rules (see `.cursor/rules/realistic_time_estimation.mdc`) +- Apply Playwright test investigation rules (see `.cursor/rules/playwright_test_investigation.mdc`) \ No newline at end of file diff --git a/.cursor/rules/legacy_dexie.mdc b/.cursor/rules/legacy_dexie.mdc deleted file mode 100644 index 82de6cca..00000000 --- a/.cursor/rules/legacy_dexie.mdc +++ /dev/null @@ -1,6 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- -All references in the codebase to Dexie apply only to migration from IndexedDb to Sqlite and will be deprecated in future versions. \ No newline at end of file diff --git a/.cursor/rules/markdown.mdc b/.cursor/rules/markdown.mdc deleted file mode 100644 index f95f297b..00000000 --- a/.cursor/rules/markdown.mdc +++ /dev/null @@ -1,332 +0,0 @@ ---- -globs: *.md -alwaysApply: false ---- -# Cursor Markdown Ruleset for TimeSafari Documentation - -## Overview - -This ruleset enforces consistent markdown formatting standards across all project -documentation, ensuring readability, maintainability, and compliance with -markdownlint best practices. - -## General Formatting Standards - -### Line Length - -- **Maximum line length**: 80 characters -- **Exception**: Code blocks (JSON, shell, TypeScript, etc.) - no line length - enforcement -- **Rationale**: Ensures readability across different screen sizes and terminal - widths - -### Blank Lines - -- **Headings**: Must be surrounded by blank lines above and below -- **Lists**: Must be surrounded by blank lines above and below -- **Code blocks**: Must be surrounded by blank lines above and below -- **Maximum consecutive blank lines**: 1 (no multiple blank lines) -- **File start**: No blank lines at the beginning of the file -- **File end**: Single newline character at the end - -### Whitespace - -- **No trailing spaces**: Remove all trailing whitespace from lines -- **No tabs**: Use spaces for indentation -- **Consistent indentation**: 2 spaces for list items and nested content - -## Heading Standards - -### Format - -- **Style**: ATX-style headings (`#`, `##`, `###`, etc.) -- **Case**: Title case for general headings -- **Code references**: Use backticks for file names and technical terms - - ✅ `### Current package.json Scripts` - - ❌ `### Current Package.json Scripts` - -### Hierarchy - -- **H1 (#)**: Document title only -- **H2 (##)**: Major sections -- **H3 (###)**: Subsections -- **H4 (####)**: Sub-subsections -- **H5+**: Avoid deeper nesting - -## List Standards - -### Unordered Lists - -- **Marker**: Use `-` (hyphen) consistently -- **Indentation**: 2 spaces for nested items -- **Blank lines**: Surround lists with blank lines - -### Ordered Lists - -- **Format**: `1.`, `2.`, `3.` (sequential numbering) -- **Indentation**: 2 spaces for nested items -- **Blank lines**: Surround lists with blank lines - -### Task Lists - -- **Format**: `- [ ]` for incomplete, `- [x]` for complete -- **Use case**: Project planning, checklists, implementation tracking - -## Code Block Standards - -### Fenced Code Blocks - -- **Syntax**: Triple backticks with language specification -- **Languages**: `json`, `bash`, `typescript`, `javascript`, `yaml`, `markdown` -- **Blank lines**: Must be surrounded by blank lines above and below -- **Line length**: No enforcement within code blocks - -### Inline Code - -- **Format**: Single backticks for inline code references -- **Use case**: File names, commands, variables, properties - -## Special Content Standards - -### JSON Examples - -```json -{ - "property": "value", - "nested": { - "property": "value" - } -} -``` - -### Shell Commands - -```bash -# Command with comment -npm run build:web - -# Multi-line command -VITE_GIT_HASH=`git log -1 --pretty=format:%h` \ - vite build --config vite.config.web.mts -``` - -### TypeScript Examples - -```typescript -// Function with JSDoc -/** - * Get environment configuration - * @param env - Environment name - * @returns Environment config object - */ -const getEnvironmentConfig = (env: string) => { - switch (env) { - case 'prod': - return { /* production settings */ }; - default: - return { /* development settings */ }; - } -}; -``` - -## File Structure Standards - -### Document Header - -```markdown -# Document Title - -**Author**: Matthew Raymer -**Date**: YYYY-MM-DD -**Status**: 🎯 **STATUS** - Brief description - -## Overview - -Brief description of the document's purpose and scope. -``` - -### Section Organization - -1. **Overview/Introduction** -2. **Current State Analysis** -3. **Implementation Plan** -4. **Technical Details** -5. **Testing & Validation** -6. **Next Steps** - -## Markdownlint Configuration - -### Required Rules - -```json -{ - "MD013": { "code_blocks": false }, - "MD012": true, - "MD022": true, - "MD031": true, - "MD032": true, - "MD047": true, - "MD009": true -} -``` - -### Rule Explanations - -- **MD013**: Line length (disabled for code blocks) -- **MD012**: No multiple consecutive blank lines -- **MD022**: Headings should be surrounded by blank lines -- **MD031**: Fenced code blocks should be surrounded by blank lines -- **MD032**: Lists should be surrounded by blank lines -- **MD047**: Files should end with a single newline -- **MD009**: No trailing spaces - -## Validation Commands - -### Check Single File - -```bash -npx markdownlint docs/filename.md -``` - -### Check All Documentation - -```bash -npx markdownlint docs/ -``` - -### Auto-fix Common Issues - -```bash -# Remove trailing spaces -sed -i 's/[[:space:]]*$//' docs/filename.md - -# Remove multiple blank lines -sed -i '/^$/N;/^\n$/D' docs/filename.md - -# Add newline at end if missing -echo "" >> docs/filename.md -``` - -## Common Patterns - -### Implementation Plans - -```markdown -## Implementation Plan - -### Phase 1: Foundation (Day 1) - -#### 1.1 Component Setup - -- [ ] Create new component file -- [ ] Add basic structure -- [ ] Implement core functionality - -#### 1.2 Configuration - -- [ ] Update configuration files -- [ ] Add environment variables -- [ ] Test configuration loading -``` - -### Status Tracking - -```markdown -**Status**: ✅ **COMPLETE** - All phases finished -**Progress**: 75% (15/20 components) -**Next**: Ready for testing phase -``` - -### Performance Metrics - -```markdown -#### 📊 Performance Metrics -- **Build Time**: 2.3 seconds (50% faster than baseline) -- **Bundle Size**: 1.2MB (30% reduction) -- **Success Rate**: 100% (no failures in 50 builds) -``` - -## Enforcement - -### Pre-commit Hooks - -- Run markdownlint on all changed markdown files -- Block commits with linting violations -- Auto-fix common issues when possible - -### CI/CD Integration - -- Include markdownlint in build pipeline -- Generate reports for documentation quality -- Fail builds with critical violations - -### Team Guidelines - -- All documentation PRs must pass markdownlint -- Use provided templates for new documents -- Follow established patterns for consistency - -## Templates - -### New Document Template - -```markdown -# Document Title - -**Author**: Matthew Raymer -**Date**: YYYY-MM-DD -**Status**: 🎯 **PLANNING** - Ready for Implementation - -## Overview - -Brief description of the document's purpose and scope. - -## Current State - -Description of current situation or problem. - -## Implementation Plan - -### Phase 1: Foundation - -- [ ] Task 1 -- [ ] Task 2 - -## Next Steps - -1. **Review and approve plan** -2. **Begin implementation** -3. **Test and validate** - ---- - -**Status**: Ready for implementation -**Priority**: Medium -**Estimated Effort**: X days -**Dependencies**: None -**Stakeholders**: Development team -``` - ---- - -**Last Updated**: 2025-07-09 -**Version**: 1.0 -**Maintainer**: Matthew Raymer - - -### Heading Uniqueness - -- **Rule**: No duplicate heading content at the same level -- **Scope**: Within a single document -- **Rationale**: Maintains clear document structure and navigation -- **Example**: - - ```markdown - ## Features ✅ - ### Authentication - ### Authorization - - ## Features ❌ (Duplicate heading) - ### Security - ### Performance - ``` \ No newline at end of file diff --git a/.cursor/rules/meta_bug_diagnosis.mdc b/.cursor/rules/meta_bug_diagnosis.mdc new file mode 100644 index 00000000..22319342 --- /dev/null +++ b/.cursor/rules/meta_bug_diagnosis.mdc @@ -0,0 +1,288 @@ +# Meta-Rule: Bug Diagnosis Workflow + +**Author**: Matthew Raymer +**Date**: August 24, 2025 +**Status**: 🎯 **ACTIVE** - Core workflow for all bug investigation + +## Purpose + +This meta-rule defines the systematic approach for investigating and diagnosing +bugs, defects, and unexpected behaviors in the TimeSafari application. It ensures +consistent, thorough, and efficient problem-solving workflows. + +## Workflow Constraints + +**This meta-rule enforces DIAGNOSIS MODE for all bundled sub-rules:** + +```json +{ + "workflowMode": "diagnosis", + "constraints": { + "mode": "read_only", + "forbidden": ["modify", "create", "build", "commit"], + "required": "complete_investigation_before_fixing" + } +} +``` + +**All bundled sub-rules automatically inherit these constraints.** + +## Workflow State Update + +**When this meta-rule is invoked, update the workflow state file:** + +```json +{ + "currentMode": "diagnosis", + "lastInvoked": "meta_bug_diagnosis.mdc", + "timestamp": "2025-01-27T15:30:00Z", + "constraints": { + "mode": "read_only", + "forbidden": ["modify", "create", "build", "commit"], + "allowed": ["read", "search", "analyze", "document"], + "required": "complete_investigation_before_fixing" + } +} +``` + +**State File Location**: `.cursor/rules/.workflow_state.json` + +**This enables the core always-on rule to enforce diagnosis mode constraints.** + +## When to Use + +**ALWAYS** - Apply this workflow to every bug investigation, regardless of +severity or complexity. This ensures systematic problem-solving and prevents +common investigation pitfalls. + +## Bundled Rules + +### **Investigation Foundation** + +- **`development/research_diagnostic.mdc`** - Research and investigation methodologies +- **`development/logging_standards.mdc`** - Logging and debugging best practices +- **`development/type_safety_guide.mdc`** - Type safety and error prevention + +### **Development Workflow** + +- **`workflow/version_control.mdc`** - Version control during investigation +- **`development/software_development.mdc`** - Development best practices + +## Critical Development Constraints + +### **🚫 NEVER Use Build Commands During Diagnosis** + +**Critical Rule**: Never use `npm run build:web` or similar build commands during bug diagnosis + +- **Reason**: These commands block the chat and prevent effective troubleshooting +- **Impact**: Blocks user interaction, prevents real-time problem solving +- **Alternative**: Use safe, fast commands for investigation +- **When to use build**: Only after diagnosis is complete and fixes are ready for testing + +### **Safe Diagnosis Commands** + +✅ **Safe to use during diagnosis:** +- `npm run lint-fix` - Syntax and style checking +- `npm run type-check` - TypeScript validation (if available) +- `git status` - Version control status +- `ls` / `dir` - File listing +- `cat` / `read_file` - File content inspection +- `grep_search` - Text pattern searching + +❌ **Never use during diagnosis:** +- `npm run build:web` - Blocks chat +- `npm run build:electron` - Blocks chat +- `npm run build:capacitor` - Blocks chat +- Any long-running build processes + +## Investigation Workflow + +### **Phase 1: Problem Definition** + +1. **Gather Evidence** + - Error messages and stack traces + - User-reported symptoms + - System logs and timestamps + - Reproduction steps + +2. **Context Analysis** + - When did the problem start? + - What changed recently? + - Which platform/environment? + - User actions leading to the issue + +### **Phase 2: Systematic Investigation** + +1. **Code Inspection** + - Relevant file examination + - Import and dependency analysis + - Syntax and type checking + - Logic flow analysis + +2. **Environment Analysis** + - Platform-specific considerations + - Configuration and settings + - Database and storage state + - Network and API connectivity + +### **Phase 3: Root Cause Identification** + +1. **Pattern Recognition** + - Similar issues in codebase + - Common failure modes + - Platform-specific behaviors + - Recent changes impact + +2. **Hypothesis Testing** + - Targeted code changes + - Configuration modifications + - Environment adjustments + - Systematic elimination + +## Investigation Techniques + +### **Safe Code Analysis** + +- **File Reading**: Use `read_file` tool for targeted inspection +- **Pattern Searching**: Use `grep_search` for code patterns +- **Semantic Search**: Use `codebase_search` for related functionality +- **Import Tracing**: Follow dependency chains systematically + +### **Error Analysis** + +- **Stack Trace Analysis**: Identify error origin and propagation +- **Log Correlation**: Match errors with system events +- **Timeline Reconstruction**: Build sequence of events +- **Context Preservation**: Maintain investigation state + +### **Platform Considerations** + +- **Web Platform**: Browser-specific behaviors and limitations +- **Electron Platform**: Desktop app considerations +- **Capacitor Platform**: Mobile app behaviors +- **Cross-Platform**: Shared vs. platform-specific code + +## Evidence Collection Standards + +### **Timestamps** + +- **UTC Format**: All timestamps in UTC for consistency +- **Precision**: Include milliseconds for precise correlation +- **Context**: Include relevant system state information +- **Correlation**: Link events across different components + +### **Error Context** + +- **Full Error Objects**: Capture complete error information +- **Stack Traces**: Preserve call stack for analysis +- **User Actions**: Document steps leading to error +- **System State**: Capture relevant configuration and state + +### **Reproduction Steps** + +- **Clear Sequence**: Step-by-step reproduction instructions +- **Environment Details**: Platform, version, configuration +- **Data Requirements**: Required data or state +- **Expected vs. Actual**: Clear behavior comparison + +## Investigation Documentation + +### **Problem Summary** + +- **Issue Description**: Clear, concise problem statement +- **Impact Assessment**: Severity and user impact +- **Scope Definition**: Affected components and users +- **Priority Level**: Based on impact and frequency + +### **Investigation Log** + +- **Timeline**: Chronological investigation steps +- **Evidence**: Collected information and findings +- **Hypotheses**: Tested theories and results +- **Conclusions**: Root cause identification + +### **Solution Requirements** + +- **Fix Description**: Required changes and approach +- **Testing Strategy**: Validation and verification steps +- **Rollback Plan**: Reversion strategy if needed +- **Prevention Measures**: Future issue prevention + +## Quality Standards + +### **Investigation Completeness** + +- **Evidence Sufficiency**: Adequate information for root cause +- **Alternative Theories**: Considered and eliminated +- **Platform Coverage**: All relevant platforms investigated +- **Edge Cases**: Unusual scenarios considered + +### **Documentation Quality** + +- **Clear Communication**: Understandable to all stakeholders +- **Technical Accuracy**: Precise technical details +- **Actionable Insights**: Clear next steps and recommendations +- **Knowledge Transfer**: Lessons learned for future reference + +## Common Pitfalls + +### **Investigation Mistakes** + +- **Jumping to Solutions**: Implementing fixes before understanding +- **Insufficient Evidence**: Making assumptions without data +- **Platform Blindness**: Ignoring platform-specific behaviors +- **Scope Creep**: Expanding investigation beyond original problem + +### **Communication Issues** + +- **Technical Jargon**: Using unclear terminology +- **Missing Context**: Insufficient background information +- **Unclear Recommendations**: Vague or ambiguous next steps +- **Poor Documentation**: Incomplete or unclear investigation records + +## Success Criteria + +- [ ] **Problem clearly defined** with sufficient evidence +- [ ] **Root cause identified** through systematic investigation +- [ ] **Solution approach determined** with clear requirements +- [ ] **Documentation complete** for knowledge transfer +- [ ] **No chat-blocking commands** used during investigation +- [ ] **Platform considerations** properly addressed +- [ ] **Timeline and context** properly documented + +## Integration with Other Meta-Rules + +### **Bug Fixing** + +- **Investigation Results**: Provide foundation for fix implementation +- **Solution Requirements**: Define what needs to be built +- **Testing Strategy**: Inform validation approach +- **Documentation**: Support implementation guidance + +### **Feature Planning** + +- **Root Cause Analysis**: Identify systemic issues +- **Prevention Measures**: Plan future issue avoidance +- **Architecture Improvements**: Identify structural enhancements +- **Process Refinements**: Improve development workflows + +### **Research and Documentation** + +- **Knowledge Base**: Contribute to troubleshooting guides +- **Pattern Recognition**: Identify common failure modes +- **Best Practices**: Develop investigation methodologies +- **Team Training**: Improve investigation capabilities + +--- + +**See also**: + +- `.cursor/rules/meta_bug_fixing.mdc` for implementing fixes +- `.cursor/rules/meta_feature_planning.mdc` for planning improvements +- `.cursor/rules/meta_documentation.mdc` for documentation standards + +**Status**: Active meta-rule for bug diagnosis +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: Development team, QA team, DevOps team diff --git a/.cursor/rules/meta_bug_fixing.mdc b/.cursor/rules/meta_bug_fixing.mdc new file mode 100644 index 00000000..538b5cae --- /dev/null +++ b/.cursor/rules/meta_bug_fixing.mdc @@ -0,0 +1,214 @@ +# Meta-Rule: Bug Fixing + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Bug fix implementation workflow bundling + +## Purpose + +This meta-rule bundles all the rules needed for implementing bug fixes +with proper testing and validation. Use this after diagnosis when +implementing the actual fix. + +## Workflow Constraints + +**This meta-rule enforces FIXING MODE for all bundled sub-rules:** + +```json +{ + "workflowMode": "fixing", + "constraints": { + "mode": "implementation", + "allowed": ["modify", "create", "build", "test", "commit"], + "required": "diagnosis_complete_before_fixing" + } +} +``` + +**All bundled sub-rules automatically inherit these constraints.** + +## Workflow State Update + +**When this meta-rule is invoked, update the workflow state file:** + +```json +{ + "currentMode": "fixing", + "lastInvoked": "meta_bug_fixing.mdc", + "timestamp": "2025-01-27T15:30:00Z", + "constraints": { + "mode": "implementation", + "allowed": ["modify", "create", "build", "test", "commit"], + "forbidden": [], + "required": "diagnosis_complete_before_fixing" + } +} +``` + +**State File Location**: `.cursor/rules/.workflow_state.json` + +**This enables the core always-on rule to enforce fixing mode constraints.** + +## When to Use + +- **Post-Diagnosis**: After root cause is identified and fix is planned +- **Fix Implementation**: When coding the actual bug fix +- **Testing & Validation**: When testing the fix works correctly +- **Code Review**: When reviewing the fix implementation +- **Deployment**: When preparing the fix for deployment +- **Documentation**: When documenting the fix and lessons learned + +## Bundled Rules + +### **Implementation Standards** + +- **`development/software_development.mdc`** - Core development + principles, evidence requirements, and testing strategy +- **`development/type_safety_guide.mdc`** - Type-safe implementation + with proper error handling and type guards +- **`development/logging_migration.mdc`** - Proper logging + implementation and migration from console.* calls + +### **Code Quality & Review** + +- **`development/historical_comment_management.mdc`** - Code quality + standards and comment transformation rules +- **`development/historical_comment_patterns.mdc`** - Specific + patterns for transforming historical comments +- **`development/complexity_assessment.mdc`** - Complexity evaluation + for fix implementation + +### **Platform & Testing** + +- **`app/timesafari_development.mdc`** - TimeSafari-specific + development workflow and testing requirements +- **`app/timesafari_platforms.mdc`** - Platform-specific testing + and validation requirements +- **`architecture/build_validation.mdc`** - Build system validation + and testing procedures + +## Workflow Sequence + +### **Phase 1: Fix Implementation (Start Here)** + +1. **Development Standards** - Apply `software_development.mdc` for + core implementation principles +2. **Type Safety** - Use `type_safety_guide.mdc` for type-safe + implementation +3. **Logging Implementation** - Apply `logging_migration.mdc` for + proper logging + +### **Phase 2: Quality & Review** + +1. **Code Quality** - Use `historical_comment_management.mdc` for + code quality standards +2. **Complexity Assessment** - Apply `complexity_assessment.mdc` to + evaluate fix complexity +3. **Code Review** - Follow review standards from bundled rules + +### **Phase 3: Testing & Validation** + +1. **Platform Testing** - Use `timesafari_platforms.mdc` for + platform-specific testing +2. **Build Validation** - Apply `build_validation.mdc` for build + system compliance +3. **Final Validation** - Verify fix works across all platforms + +## Success Criteria + +- [ ] **Fix implemented** following development standards +- [ ] **Type safety maintained** with proper error handling +- [ ] **Logging properly implemented** with component context +- [ ] **Code quality standards met** with clean, maintainable code +- [ ] **Testing completed** across all target platforms +- [ ] **Build validation passed** with no build system issues +- [ ] **Code review completed** with all feedback addressed +- [ ] **Documentation updated** with fix details and lessons learned + +## Common Pitfalls + +- **Don't skip type safety** - leads to runtime errors +- **Don't ignore logging** - makes future debugging harder +- **Don't skip platform testing** - misses platform-specific issues +- **Don't ignore code quality** - creates technical debt +- **Don't skip build validation** - can break build system +- **Don't forget documentation** - loses fix context for future + +## Integration Points + +### **With Other Meta-Rules** + +- **Bug Diagnosis**: Investigation results drive fix implementation +- **Feature Implementation**: Fix patterns inform future development +- **Feature Planning**: Fix complexity informs future planning + +### **With Development Workflow** + +- Fix implementation follows development standards +- Testing strategy ensures fix quality +- Code review maintains code quality + +## Feedback & Improvement + +### **Sub-Rule Ratings (1-5 scale)** + +- **Development Standards**: ___/5 - Comments: _______________ +- **Type Safety**: ___/5 - Comments: _______________ +- **Logging Migration**: ___/5 - Comments: _______________ +- **Code Quality**: ___/5 - Comments: _______________ +- **Platform Testing**: ___/5 - Comments: _______________ + +### **Workflow Feedback** + +- **Implementation Clarity**: How clear was the implementation guidance? +- **Testing Coverage**: Were testing requirements sufficient or excessive? +- **Process Effectiveness**: How well did the workflow work for you? + +### **Sub-Rule Improvements** + +- **Clarity Issues**: Which rules were unclear or confusing? +- **Missing Examples**: What examples would make rules more useful? +- **Integration Problems**: Do any rules conflict or overlap? + +### **Overall Experience** + +- **Time Saved**: How much time did this meta-rule save you? +- **Quality Improvement**: Did following these rules improve your fix? +- **Recommendation**: Would you recommend this meta-rule to others? + +## Model Implementation Checklist + +### Before Bug Fixing + +- [ ] **Root Cause Understood**: Confirm root cause is clearly identified +- [ ] **Fix Strategy Planned**: Plan implementation approach and testing +- [ ] **Platform Impact Assessed**: Understand impact across all platforms +- [ ] **Testing Strategy Planned**: Plan testing approach for the fix + +### During Bug Fixing + +- [ ] **Rule Application**: Apply bundled rules in recommended sequence +- [ ] **Implementation**: Implement fix following development standards +- [ ] **Testing**: Test fix across all target platforms +- [ ] **Documentation**: Document implementation details and decisions + +### After Bug Fixing + +- [ ] **Validation**: Verify fix meets all success criteria +- [ ] **Code Review**: Complete code review with team +- [ ] **Deployment**: Deploy fix following deployment procedures +- [ ] **Feedback Collection**: Collect feedback on meta-rule effectiveness + +--- + +**See also**: + +- `.cursor/rules/meta_bug_diagnosis.mdc` for investigation workflow +- `.cursor/rules/meta_feature_implementation.mdc` for implementation patterns +- `.cursor/rules/meta_feature_planning.mdc` for planning future work + +**Status**: Active meta-rule for bug fixing +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: Development team, QA team, DevOps team diff --git a/.cursor/rules/meta_change_evaluation.mdc b/.cursor/rules/meta_change_evaluation.mdc new file mode 100644 index 00000000..dd4e1f74 --- /dev/null +++ b/.cursor/rules/meta_change_evaluation.mdc @@ -0,0 +1,383 @@ +# Meta-Rule: Change Evaluation and Breaking Change Detection + +**Author**: Matthew Raymer +**Date**: 2025-08-25 +**Status**: 🎯 **ACTIVE** - Manually activated change evaluation rule + +## Purpose + +This meta-rule provides a systematic approach to evaluate changes between +branches and detect potential breaking changes. It's designed to catch +problematic model behavior by analyzing the nature, scope, and impact of +code changes before they cause issues. + +## When to Use + +**Manual Activation Only** - This rule should be invoked when: + +- Reviewing changes before merging branches +- Investigating unexpected behavior after updates +- Validating that model-generated changes are safe +- Analyzing the impact of recent commits +- Debugging issues that may be caused by recent changes + +## Workflow State Enforcement + +**This meta-rule enforces current workflow mode constraints:** + +### **Current Workflow State** + +```json +{ + "workflowState": { + "currentMode": "diagnosis|fixing|planning|research|documentation", + "constraints": { + "mode": "read_only|implementation|design_only|investigation|writing_only", + "allowed": ["array", "of", "allowed", "actions"], + "forbidden": ["array", "of", "forbidden", "actions"] + } + } +} +``` + +### **Mode-Specific Enforcement** + +**Diagnosis Mode (read_only):** + +- ❌ **Forbidden**: File modification, code creation, build commands, git + commits +- ✅ **Allowed**: File reading, code analysis, investigation, documentation +- **Response**: Focus on analysis and documentation, not implementation + +**Fixing Mode (implementation):** + +- ✅ **Allowed**: File modification, code creation, build commands, testing, + git commits +- ❌ **Forbidden**: None (full implementation mode) +- **Response**: Proceed with implementation and testing + +**Planning Mode (design_only):** + +- ❌ **Forbidden**: Implementation, coding, building, deployment +- ✅ **Allowed**: Analysis, design, estimation, documentation, architecture +- **Response**: Focus on planning and design, not implementation + +**Research Mode (investigation):** + +- ❌ **Forbidden**: File modification, implementation, deployment +- ✅ **Allowed**: Investigation, analysis, research, documentation +- **Response**: Focus on investigation and analysis + +**Documentation Mode (writing_only):** + +- ❌ **Forbidden**: Implementation, coding, building, deployment +- ✅ **Allowed**: Writing, editing, formatting, structuring, reviewing +- **Response**: Focus on documentation creation and improvement + +## Change Evaluation Process + +### **Phase 1: Change Discovery and Analysis** + +1. **Branch Comparison Analysis** + + - Compare working branch with master/main branch + - Identify all changed files and their modification types + - Categorize changes by scope and impact + +2. **Change Pattern Recognition** + + - Identify common change patterns (refactoring, feature addition, bug + fixes) + - Detect unusual or suspicious change patterns + - Flag changes that deviate from established patterns + +3. **Dependency Impact Assessment** + + - Analyze changes to imports, exports, and interfaces + - Identify potential breaking changes to public APIs + - Assess impact on dependent components and services + +### **Phase 2: Breaking Change Detection** + +1. **API Contract Analysis** + + - Check for changes to function signatures, method names, class + interfaces + - Identify removed or renamed public methods/properties + - Detect changes to configuration options and constants + +2. **Data Structure Changes** + + - Analyze database schema modifications + - Check for changes to data models and interfaces + - Identify modifications to serialization/deserialization logic + +3. **Behavioral Changes** + + - Detect changes to business logic and algorithms + - Identify modifications to error handling and validation + - Check for changes to user experience and workflows + +### **Phase 3: Risk Assessment and Recommendations** + +1. **Risk Level Classification** + + - **LOW**: Cosmetic changes, documentation updates, minor refactoring + - **MEDIUM**: Internal API changes, configuration modifications, + performance improvements + - **HIGH**: Public API changes, breaking interface modifications, major + architectural changes + - **CRITICAL**: Database schema changes, authentication modifications, + security-related changes + +2. **Impact Analysis** + + - Identify affected user groups and use cases + - Assess potential for data loss or corruption + - Evaluate impact on system performance and reliability + +3. **Mitigation Strategies** + + - Recommend testing approaches for affected areas + - Suggest rollback strategies if needed + - Identify areas requiring additional validation + +## Implementation Guidelines + +### **Change Analysis Tools** + +1. **Git Diff Analysis** + + ```bash + # Compare working branch with master + git diff master..HEAD --name-only + git diff master..HEAD --stat + git log master..HEAD --oneline + ``` + +2. **File Change Categorization** + + - **Core Files**: Application entry points, main services, critical + utilities + - **Interface Files**: Public APIs, component interfaces, data models + - **Configuration Files**: Environment settings, build configurations, + deployment scripts + - **Test Files**: Unit tests, integration tests, test utilities + +3. **Change Impact Mapping** + + - Map changed files to affected functionality + - Identify cross-dependencies and ripple effects + - Document potential side effects and unintended consequences + +### **Breaking Change Detection Patterns** + +1. **Function Signature Changes** + + ```typescript + // BEFORE + function processData(data: string, options?: Options): Result + + // AFTER - BREAKING CHANGE + function processData(data: string, options: Required<Options>): Result + ``` + +2. **Interface Modifications** + + ```typescript + // BEFORE + interface UserProfile { + name: string; + email: string; + } + + // AFTER - BREAKING CHANGE + interface UserProfile { + name: string; + email: string; + phone: string; // Required new field + } + ``` + +3. **Configuration Changes** + + ```typescript + // BEFORE + const config = { + apiUrl: 'https://api.example.com', + timeout: 5000 + }; + + // AFTER - BREAKING CHANGE + const config = { + apiUrl: 'https://api.example.com', + timeout: 5000, + retries: 3 // New required configuration + }; + ``` + +## Output Format + +### **Change Evaluation Report** + +```markdown +# Change Evaluation Report + +## Executive Summary + +- **Risk Level**: [LOW|MEDIUM|HIGH|CRITICAL] +- **Overall Assessment**: [SAFE|CAUTION|DANGEROUS|CRITICAL] +- **Recommendation**: [PROCEED|REVIEW|HALT|IMMEDIATE_ROLLBACK] + +## Change Analysis + +### Files Modified + +- **Total Changes**: [X] files +- **Core Files**: [X] files +- **Interface Files**: [X] files +- **Configuration Files**: [X] files +- **Test Files**: [X] files + +### Change Categories + +- **Refactoring**: [X] changes +- **Feature Addition**: [X] changes +- **Bug Fixes**: [X] changes +- **Configuration**: [X] changes +- **Documentation**: [X] changes + +## Breaking Change Detection + +### API Contract Changes + +- **Function Signatures**: [X] modified +- **Interface Definitions**: [X] modified +- **Public Methods**: [X] added/removed/modified + +### Data Structure Changes + +- **Database Schema**: [X] modifications +- **Data Models**: [X] changes +- **Serialization**: [X] changes + +### Behavioral Changes + +- **Business Logic**: [X] modifications +- **Error Handling**: [X] changes +- **User Experience**: [X] changes + +## Risk Assessment + +### Impact Analysis + +- **User Groups Affected**: [Description] +- **Use Cases Impacted**: [Description] +- **Performance Impact**: [Description] +- **Reliability Impact**: [Description] + +### Dependencies + +- **Internal Dependencies**: [List] +- **External Dependencies**: [List] +- **Configuration Dependencies**: [List] + +## Recommendations + +### Testing Requirements + +- [ ] Unit tests for modified components +- [ ] Integration tests for affected workflows +- [ ] Performance tests for changed algorithms +- [ ] User acceptance tests for UI changes + +### Validation Steps + +- [ ] Code review by domain experts +- [ ] API compatibility testing +- [ ] Database migration testing +- [ ] End-to-end workflow testing + +### Rollback Strategy + +- **Rollback Complexity**: [LOW|MEDIUM|HIGH] +- **Rollback Time**: [Estimated time] +- **Data Preservation**: [Strategy description] + +## Conclusion + +[Summary of findings and final recommendation] +``` + +## Usage Examples + +### **Example 1: Safe Refactoring** + +```bash +@meta_change_evaluation.mdc analyze changes between feature-branch and master +``` + +### **Example 2: Breaking Change Investigation** + +```bash +@meta_change_evaluation.mdc evaluate potential breaking changes in recent commits +``` + +### **Example 3: Pre-Merge Validation** + +```bash +@meta_change_evaluation.mdc validate changes before merging feature-branch to master +``` + +## Success Criteria + +- [ ] **Change Discovery**: All modified files are identified and categorized +- [ ] **Pattern Recognition**: Unusual change patterns are detected and flagged +- [ ] **Breaking Change Detection**: All potential breaking changes are identified +- [ ] **Risk Assessment**: Accurate risk levels are assigned with justification +- [ ] **Recommendations**: Actionable recommendations are provided +- [ ] **Documentation**: Complete change evaluation report is generated + +## Common Pitfalls + +- **Missing Dependencies**: Failing to identify all affected components +- **Underestimating Impact**: Not considering ripple effects of changes +- **Incomplete Testing**: Missing critical test scenarios for changes +- **Configuration Blindness**: Overlooking configuration file changes +- **Interface Assumptions**: Assuming internal changes won't affect external + users + +## Integration with Other Meta-Rules + +### **With Bug Diagnosis** + +- Use change evaluation to identify recent changes that may have caused + bugs +- Correlate change patterns with reported issues + +### **With Feature Planning** + +- Evaluate the impact of planned changes before implementation +- Identify potential breaking changes early in the planning process + +### **With Bug Fixing** + +- Validate that fixes don't introduce new breaking changes +- Ensure fixes maintain backward compatibility + +--- + +**See also**: + +- `.cursor/rules/meta_core_always_on.mdc` for core always-on rules +- `.cursor/rules/meta_feature_planning.mdc` for feature development + workflows +- `.cursor/rules/meta_bug_diagnosis.mdc` for bug investigation workflows +- `.cursor/rules/meta_bug_fixing.mdc` for fix implementation workflows + +**Status**: Active change evaluation meta-rule +**Priority**: High (applies to all change evaluation tasks) +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: Development team, Quality Assurance team, Release +Management team diff --git a/.cursor/rules/meta_core_always_on.mdc b/.cursor/rules/meta_core_always_on.mdc new file mode 100644 index 00000000..ac9ee1dd --- /dev/null +++ b/.cursor/rules/meta_core_always_on.mdc @@ -0,0 +1,311 @@ +--- +alwaysApply: true +--- + +# Meta-Rule: Core Always-On Rules + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Core rules for every prompt + +## Purpose + +This meta-rule bundles the core rules that should be applied to **every single +prompt** because they define fundamental behaviors, principles, and context +that are essential for all AI interactions. + +## Workflow Constraints + +**This meta-rule enforces ALWAYS-ON MODE for all bundled sub-rules:** + +```json +{ + "workflowMode": "always_on", + "constraints": { + "mode": "foundation", + "alwaysApplied": true, + "required": "applied_to_every_prompt" + } +} +``` + +**All bundled sub-rules automatically inherit these constraints.** + +## Workflow State Enforcement + +**This meta-rule enforces current workflow mode constraints for all interactions:** + +### **Current Workflow State** +```json +{ + "workflowState": { + "currentMode": "diagnosis|fixing|planning|research|documentation", + "constraints": { + "mode": "read_only|implementation|design_only|investigation|writing_only", + "allowed": ["array", "of", "allowed", "actions"], + "forbidden": ["array", "of", "forbidden", "actions"] + } + } +} +``` + +### **Constraint Enforcement Rules** + +**Before responding to any user request, enforce current mode constraints:** + +1. **Read current workflow state** from `.cursor/rules/.workflow_state.json` +2. **Identify current mode** and its constraints +3. **Validate user request** against current mode constraints +4. **Enforce constraints** before generating response +5. **Guide model behavior** based on current mode + +### **Mode-Specific Enforcement** + +**Diagnosis Mode (read_only):** +- ❌ **Forbidden**: File modification, code creation, build commands, git commits +- ✅ **Allowed**: File reading, code analysis, investigation, documentation +- **Response**: Guide user toward investigation and analysis, not implementation + +**Fixing Mode (implementation):** +- ✅ **Allowed**: File modification, code creation, build commands, testing, git commits +- ❌ **Forbidden**: None (full implementation mode) +- **Response**: Proceed with implementation and testing + +**Planning Mode (design_only):** +- ❌ **Forbidden**: Implementation, coding, building, deployment +- ✅ **Allowed**: Analysis, design, estimation, documentation, architecture +- **Response**: Focus on planning and design, not implementation + +**Research Mode (investigation):** +- ❌ **Forbidden**: File modification, implementation, deployment +- ✅ **Allowed**: Investigation, analysis, research, documentation +- **Response**: Focus on investigation and analysis + +**Documentation Mode (writing_only):** +- ❌ **Forbidden**: Implementation, coding, building, deployment +- ✅ **Allowed**: Writing, editing, formatting, structuring, reviewing +- **Response**: Focus on documentation creation and improvement + +### **Constraint Violation Response** + +**If user request violates current mode constraints:** + +``` +❌ **WORKFLOW CONSTRAINT VIOLATION** + +**Current Mode**: [MODE_NAME] +**Requested Action**: [ACTION] +**Constraint Violation**: [DESCRIPTION] + +**What You Can Do Instead**: +- [LIST OF ALLOWED ALTERNATIVES] + +**To Enable This Action**: Invoke @meta_[appropriate_mode].mdc +``` + +### **Mode Transition Guidance** + +**When user needs to change modes, provide clear guidance:** + +``` +🔄 **MODE TRANSITION REQUIRED** + +**Current Mode**: [CURRENT_MODE] +**Required Mode**: [REQUIRED_MODE] +**Action**: Invoke @meta_[required_mode].mdc + +**This will enable**: [DESCRIPTION OF NEW CAPABILITIES] +``` + +## When to Use + +**ALWAYS** - These rules apply to every single prompt, regardless of the task +or context. They form the foundation for all AI assistant behavior. + +## Bundled Rules + +### **Core Human Competence Principles** + +- **`core/base_context.mdc`** - Human competence first principles, interaction + guidelines, and output contract requirements +- **`core/less_complex.mdc`** - Minimalist solution principle and complexity + guidelines + +### **Time & Context Standards** + +- **`development/time.mdc`** - Time handling principles and UTC standards +- **`development/time_examples.mdc`** - Practical time implementation examples +- **`development/time_implementation.mdc`** - Detailed time implementation + guidelines + +### **Version Control & Process** + +- **`workflow/version_control.mdc`** - Version control principles and commit + guidelines +- **`workflow/commit_messages.mdc`** - Commit message format and conventions + +### **Application Context** + +- **`app/timesafari.mdc`** - Core TimeSafari application context and + development principles +- **`app/timesafari_development.mdc`** - TimeSafari-specific development + workflow and quality standards + +## Why These Rules Are Always-On + +### **Base Context** + +- **Human Competence First**: Every interaction must increase human competence +- **Output Contract**: All responses must follow the required structure +- **Competence Hooks**: Learning and collaboration must be built into every response + +### **Time Standards** + +- **UTC Consistency**: All timestamps must use UTC for system operations +- **Evidence Collection**: Time context is essential for debugging and investigation +- **Cross-Platform**: Time handling affects all platforms and features + +### **Version Control** + +- **Commit Standards**: Every code change must follow commit message conventions +- **Process Consistency**: Version control affects all development work +- **Team Collaboration**: Commit standards enable effective team communication + +### **Application Context** + +- **Platform Awareness**: Every task must consider web/mobile/desktop platforms +- **Architecture Principles**: All work must follow TimeSafari patterns +- **Development Standards**: Quality and testing requirements apply to all work + +## Application Priority + +### **Primary (Apply First)** + +1. **Base Context** - Human competence and output contract +2. **Time Standards** - UTC and timestamp requirements +3. **Application Context** - TimeSafari principles and platforms + +### **Secondary (Apply as Needed)** + +1. **Version Control** - When making code changes +2. **Complexity Guidelines** - When evaluating solution approaches + +## Integration with Other Meta-Rules + +### **Feature Planning** + +- Base context ensures human competence focus +- Time standards inform planning and estimation +- Application context drives platform considerations + +### **Bug Diagnosis** + +- Base context ensures systematic investigation +- Time standards enable proper evidence collection +- Application context provides system understanding + +### **Bug Fixing** + +- Base context ensures quality implementation +- Time standards maintain logging consistency +- Application context guides testing strategy + +### **Feature Implementation** + +- Base context ensures proper development approach +- Time standards maintain system consistency +- Application context drives architecture decisions + +## Success Criteria + +- [ ] **Base context applied** to every single prompt +- [ ] **Time standards followed** for all timestamps and logging +- [ ] **Version control standards** applied to all code changes +- [ ] **Application context considered** for all platform work +- [ ] **Human competence focus** maintained in all interactions +- [ ] **Output contract structure** followed in all responses + +## Common Pitfalls + +- **Don't skip base context** - loses human competence focus +- **Don't ignore time standards** - creates inconsistent timestamps +- **Don't forget application context** - misses platform considerations +- **Don't skip version control** - creates inconsistent commit history +- **Don't lose competence focus** - reduces learning value + +## Feedback & Improvement + +### **Rule Effectiveness Ratings (1-5 scale)** + +- **Base Context**: ___/5 - Comments: _______________ +- **Time Standards**: ___/5 - Comments: _______________ +- **Version Control**: ___/5 - Comments: _______________ +- **Application Context**: ___/5 - Comments: _______________ + +### **Always-On Effectiveness** + +- **Consistency**: Are these rules applied consistently across all prompts? +- **Value**: Do these rules add value to every interaction? +- **Overhead**: Are these rules too burdensome for simple tasks? + +### **Integration Feedback** + +- **With Other Meta-Rules**: How well do these integrate with workflow rules? +- **Context Switching**: Do these rules help or hinder context switching? +- **Learning Curve**: Are these rules easy for new users to understand? + +### **Overall Experience** + +- **Quality Improvement**: Do these rules improve response quality? +- **Efficiency**: Do these rules make interactions more efficient? +- **Recommendation**: Would you recommend keeping these always-on? + +## Model Implementation Checklist + +### Before Every Prompt + +- [ ] **Base Context**: Ensure human competence principles are active +- [ ] **Time Standards**: Verify UTC and timestamp requirements are clear +- [ ] **Application Context**: Confirm TimeSafari context is loaded +- [ ] **Version Control**: Prepare commit standards if code changes are needed +- [ ] **Workflow State**: Read current mode constraints from state file +- [ ] **Constraint Validation**: Validate user request against current mode + +### During Response Creation + +- [ ] **Output Contract**: Follow required response structure +- [ ] **Competence Hooks**: Include learning and collaboration elements +- [ ] **Time Consistency**: Apply UTC standards for all time references +- [ ] **Platform Awareness**: Consider all target platforms +- [ ] **Mode Enforcement**: Apply current mode constraints to response +- [ ] **Constraint Violations**: Block forbidden actions and guide alternatives + +### After Response Creation + +- [ ] **Validation**: Verify all always-on rules were applied +- [ ] **Quality Check**: Ensure response meets competence standards +- [ ] **Context Review**: Confirm application context was properly considered +- [ ] **Feedback Collection**: Note any issues with always-on application +- [ ] **Mode Compliance**: Verify response stayed within current mode constraints +- [ ] **Transition Guidance**: Provide clear guidance for mode changes if needed + +--- + +**See also**: + +- `.cursor/rules/meta_feature_planning.mdc` for workflow-specific rules +- `.cursor/rules/meta_bug_diagnosis.mdc` for investigation workflows +- `.cursor/rules/meta_bug_fixing.mdc` for fix implementation +- `.cursor/rules/meta_feature_implementation.mdc` for feature development + +**Status**: Active core always-on meta-rule +**Priority**: Critical (applies to every prompt) +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: All AI interactions, Development team + +**Dependencies**: All bundled sub-rules +**Stakeholders**: All AI interactions, Development team + +**Dependencies**: All bundled sub-rules +**Stakeholders**: All AI interactions, Development team diff --git a/.cursor/rules/meta_documentation.mdc b/.cursor/rules/meta_documentation.mdc new file mode 100644 index 00000000..56c44000 --- /dev/null +++ b/.cursor/rules/meta_documentation.mdc @@ -0,0 +1,275 @@ +# Meta-Rule: Documentation Writing & Education + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Documentation writing and education workflow + +## Purpose + +This meta-rule bundles documentation-related rules to create comprehensive, +educational documentation that increases human competence rather than just +providing technical descriptions. + +## Workflow Constraints + +**This meta-rule enforces DOCUMENTATION MODE for all bundled sub-rules:** + +```json +{ + "workflowMode": "documentation", + "constraints": { + "mode": "writing_only", + "allowed": ["write", "edit", "format", "structure", "review"], + "forbidden": ["implement", "code", "build", "deploy"] + } +} +``` + +**All bundled sub-rules automatically inherit these constraints.** + +## Workflow State Update + +**When this meta-rule is invoked, update the workflow state file:** + +```json +{ + "currentMode": "documentation", + "lastInvoked": "meta_documentation.mdc", + "timestamp": "2025-01-27T15:30:00Z", + "constraints": { + "mode": "writing_only", + "allowed": ["write", "edit", "format", "structure", "review"], + "forbidden": ["implement", "code", "build", "deploy"] + } +} +``` + +**State File Location**: `.cursor/rules/.workflow_state.json` + +**This enables the core always-on rule to enforce documentation mode constraints.** + +## When to Use + +**Use this meta-rule when**: +- Writing new documentation +- Updating existing documentation +- Creating technical guides +- Writing migration documentation +- Creating architectural documentation +- Writing user guides or tutorials + +## Bundled Rules + +### **Core Documentation Standards** + +- **`docs/markdown_core.mdc`** - Core markdown formatting and automation +- **`docs/markdown_templates.mdc`** - Document templates and structure +- **`docs/markdown_workflow.mdc`** - Documentation validation workflows + +### **Documentation Principles** + +- **`core/base_context.mdc`** - Human competence first principles +- **`core/less_complex.mdc`** - Minimalist solution guidelines +- **`development/software_development.mdc`** - Development documentation standards + +### **Context-Specific Rules** + +- **`app/timesafari.mdc`** - TimeSafari application context +- **`app/timesafari_development.mdc`** - Development documentation patterns +- **`architecture/architectural_patterns.mdc`** - Architecture documentation + +## Core Documentation Philosophy + +### **Education Over Technical Description** + +**Primary Goal**: Increase human competence and understanding +**Secondary Goal**: Provide accurate technical information +**Approach**: Explain the "why" before the "how" + +### **Human Competence Principles** + +1. **Context First**: Explain the problem before the solution +2. **Learning Path**: Structure content for progressive understanding +3. **Real Examples**: Use concrete, relatable examples +4. **Common Pitfalls**: Warn about typical mistakes and misconceptions +5. **Decision Context**: Explain why certain choices were made + +### **Documentation Hierarchy** + +1. **Conceptual Understanding** - What is this and why does it matter? +2. **Context and Motivation** - When and why would you use this? +3. **Technical Implementation** - How do you implement it? +4. **Examples and Patterns** - What does it look like in practice? +5. **Troubleshooting** - What can go wrong and how to fix it? + +## Implementation Guidelines + +### **Document Structure** + +**Mandatory Sections**: +- **Overview**: Clear purpose and scope with educational context +- **Why This Matters**: Business value and user benefit explanation +- **Core Concepts**: Fundamental understanding before implementation +- **Implementation**: Step-by-step technical guidance +- **Examples**: Real-world usage patterns +- **Common Issues**: Troubleshooting and prevention +- **Next Steps**: Where to go from here + +**Optional Sections**: +- **Background**: Historical context and evolution +- **Alternatives**: Other approaches and trade-offs +- **Advanced Topics**: Deep dive into complex scenarios +- **References**: Additional learning resources + +### **Writing Style** + +**Educational Approach**: +- **Conversational tone**: Write as if explaining to a colleague +- **Progressive disclosure**: Start simple, add complexity gradually +- **Active voice**: "You can do this" not "This can be done" +- **Question format**: "What happens when..." to engage thinking +- **Analogies**: Use familiar concepts to explain complex ideas + +**Technical Accuracy**: +- **Precise language**: Use exact technical terms consistently +- **Code examples**: Working, tested code snippets +- **Version information**: Specify applicable versions and platforms +- **Limitations**: Clearly state what the solution doesn't do + +### **Content Quality Standards** + +**Educational Value**: +- [ ] **Concept clarity**: Reader understands the fundamental idea +- [ ] **Context relevance**: Reader knows when to apply the knowledge +- [ ] **Practical application**: Reader can implement the solution +- [ ] **Problem prevention**: Reader avoids common mistakes +- [ ] **Next steps**: Reader knows where to continue learning + +**Technical Accuracy**: +- [ ] **Fact verification**: All technical details are correct +- [ ] **Code validation**: Examples compile and run correctly +- [ ] **Version compatibility**: Platform and version requirements clear +- [ ] **Security consideration**: Security implications addressed +- [ ] **Performance notes**: Performance characteristics documented + +## Document Types and Templates + +### **Technical Guides** + +**Focus**: Implementation and technical details +**Structure**: Problem → Solution → Implementation → Examples +**Education**: Explain the "why" behind technical choices + +### **Migration Documentation** + +**Focus**: Process and workflow guidance +**Structure**: Context → Preparation → Steps → Validation → Troubleshooting +**Education**: Help users understand migration benefits and risks + +### **Architecture Documentation** + +**Focus**: System design and decision rationale +**Structure**: Problem → Constraints → Alternatives → Decision → Implementation +**Education**: Explain design trade-offs and decision factors + +### **User Guides** + +**Focus**: Task completion and user empowerment +**Structure**: Goal → Prerequisites → Steps → Verification → Next Steps +**Education**: Help users understand the system's capabilities + +## Quality Assurance + +### **Review Checklist** + +**Educational Quality**: +- [ ] **Clear learning objective**: What will the reader learn? +- [ ] **Appropriate complexity**: Matches target audience knowledge +- [ ] **Progressive disclosure**: Information builds logically +- [ ] **Practical examples**: Real-world scenarios and use cases +- [ ] **Common questions**: Anticipates and answers reader questions + +**Technical Quality**: +- [ ] **Accuracy**: All technical details verified +- [ ] **Completeness**: Covers all necessary information +- [ ] **Consistency**: Terminology and formatting consistent +- [ ] **Currency**: Information is up-to-date +- [ ] **Accessibility**: Clear for target audience + +### **Validation Workflows** + +1. **Content Review**: Subject matter expert review +2. **Educational Review**: Learning effectiveness assessment +3. **Technical Review**: Accuracy and completeness validation +4. **User Testing**: Real user comprehension testing +5. **Continuous Improvement**: Regular updates based on feedback + +## Success Metrics + +### **Educational Effectiveness** + +- **Comprehension**: Users understand the concepts +- **Application**: Users can implement the solutions +- **Confidence**: Users feel capable and empowered +- **Efficiency**: Users complete tasks faster +- **Satisfaction**: Users find documentation helpful + +### **Technical Quality** + +- **Accuracy**: Zero technical errors +- **Completeness**: All necessary information included +- **Consistency**: Uniform style and format +- **Maintainability**: Easy to update and extend +- **Accessibility**: Clear for target audience + +## Common Pitfalls + +### **Educational Mistakes** + +- **Assumption overload**: Assuming too much prior knowledge +- **Information dump**: Overwhelming with details +- **Context missing**: Not explaining why something matters +- **Example poverty**: Insufficient practical examples +- **Feedback missing**: No way to verify understanding + +### **Technical Mistakes** + +- **Outdated information**: Not keeping content current +- **Incomplete coverage**: Missing important details +- **Inconsistent terminology**: Using different terms for same concepts +- **Poor examples**: Non-working or confusing code +- **Missing validation**: No way to verify correctness + +## Feedback and Improvement + +### **Continuous Learning** + +- **User feedback**: Collect and analyze user comments +- **Usage metrics**: Track document usage and effectiveness +- **Review cycles**: Regular content review and updates +- **Community input**: Engage users in documentation improvement +- **Best practices**: Stay current with documentation standards + +### **Quality Metrics** + +- **Readability scores**: Measure content clarity +- **User satisfaction**: Survey-based quality assessment +- **Task completion**: Success rate of documented procedures +- **Support reduction**: Decrease in help requests +- **Knowledge retention**: Long-term user understanding + +--- + +**See also**: + +- `.cursor/rules/docs/markdown_core.mdc` for core formatting standards +- `.cursor/rules/docs/markdown_templates.mdc` for document templates +- `.cursor/rules/docs/markdown_workflow.mdc` for validation workflows +- `.cursor/rules/docs/meta_rule_usage_guide.md` for how to use meta-rules +- `.cursor/rules/core/base_context.mdc` for human competence principles + +**Status**: Active documentation meta-rule +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: Documentation team, Development team, Users diff --git a/.cursor/rules/meta_feature_implementation.mdc b/.cursor/rules/meta_feature_implementation.mdc new file mode 100644 index 00000000..ff165969 --- /dev/null +++ b/.cursor/rules/meta_feature_implementation.mdc @@ -0,0 +1,226 @@ +# Meta-Rule: Feature Implementation + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Feature implementation workflow bundling + +## Purpose + +This meta-rule bundles all the rules needed for building features with +proper architecture and cross-platform support. Use this when implementing +planned features or refactoring existing code. + +## Workflow Constraints + +**This meta-rule enforces IMPLEMENTATION MODE for all bundled sub-rules:** + +```json +{ + "workflowMode": "implementation", + "constraints": { + "mode": "development", + "allowed": ["code", "build", "test", "refactor", "deploy"], + "required": "planning_complete_before_implementation" + } +} +``` + +**All bundled sub-rules automatically inherit these constraints.** + +## Workflow State Update + +**When this meta-rule is invoked, update the workflow state file:** + +```json +{ + "currentMode": "implementation", + "lastInvoked": "meta_feature_implementation.mdc", + "timestamp": "2025-01-27T15:30:00Z", + "constraints": { + "mode": "development", + "allowed": ["code", "build", "test", "refactor", "deploy"], + "forbidden": [], + "required": "planning_complete_before_implementation" + } +} +``` + +**State File Location**: `.cursor/rules/.workflow_state.json` + +**This enables the core always-on rule to enforce implementation mode constraints.** + +## When to Use + +- **Feature Development**: Building new features from planning +- **Code Refactoring**: Restructuring existing code for better architecture +- **Platform Expansion**: Adding features to new platforms +- **Service Implementation**: Building new services or components +- **Integration Work**: Connecting features with existing systems +- **Performance Optimization**: Improving feature performance + +## Bundled Rules + +### **Development Foundation** + +- **`app/timesafari_development.mdc`** - TimeSafari-specific + development workflow and quality standards +- **`development/software_development.mdc`** - Core development + principles and evidence requirements +- **`development/type_safety_guide.mdc`** - Type-safe implementation + with proper error handling + +### **Architecture & Patterns** + +- **`app/architectural_patterns.mdc`** - Design patterns and + architectural examples for features +- **`app/architectural_examples.mdc`** - Implementation examples + and testing strategies +- **`app/architectural_implementation.mdc`** - Implementation + guidelines and best practices + +### **Platform & Services** + +- **`app/timesafari_platforms.mdc`** - Platform abstraction + patterns and platform-specific requirements +- **`development/asset_configuration.mdc`** - Asset management + and build integration +- **`development/logging_standards.mdc`** - Proper logging + implementation standards + +### **Quality & Validation** + +- **`architecture/build_validation.mdc`** - Build system + validation and testing procedures +- **`architecture/build_testing.mdc`** - Testing requirements + and feedback collection +- **`development/complexity_assessment.mdc`** - Complexity + evaluation for implementation + +## Workflow Sequence + +### **Phase 1: Implementation Foundation (Start Here)** + +1. **Development Workflow** - Use `timesafari_development.mdc` for + development standards and workflow +2. **Type Safety** - Apply `type_safety_guide.mdc` for type-safe + implementation +3. **Architecture Patterns** - Use `architectural_patterns.mdc` for + design patterns + +### **Phase 2: Feature Development** + +1. **Platform Services** - Apply `timesafari_platforms.mdc` for + platform abstraction +2. **Implementation Examples** - Use `architectural_examples.mdc` + for implementation guidance +3. **Asset Configuration** - Apply `asset_configuration.mdc` for + asset management + +### **Phase 3: Quality & Testing** + +1. **Logging Implementation** - Use `logging_standards.mdc` for + proper logging +2. **Build Validation** - Apply `build_validation.mdc` for build + system compliance +3. **Testing & Feedback** - Use `build_testing.mdc` for testing + requirements + +## Success Criteria + +- [ ] **Feature implemented** following development standards +- [ ] **Type safety maintained** with proper error handling +- [ ] **Architecture patterns applied** consistently +- [ ] **Platform abstraction implemented** correctly +- [ ] **Logging properly implemented** with component context +- [ ] **Assets configured** and integrated with build system +- [ ] **Build validation passed** with no build system issues +- [ ] **Testing completed** across all target platforms +- [ ] **Code review completed** with all feedback addressed + +## Common Pitfalls + +- **Don't skip architecture patterns** - leads to inconsistent design +- **Don't ignore platform abstraction** - creates platform-specific code +- **Don't skip type safety** - leads to runtime errors +- **Don't ignore logging** - makes future debugging harder +- **Don't skip build validation** - can break build system +- **Don't forget asset configuration** - leads to missing assets + +## Integration Points + +### **With Other Meta-Rules** + +- **Feature Planning**: Planning outputs drive implementation approach +- **Bug Fixing**: Implementation patterns inform fix strategies +- **Bug Diagnosis**: Implementation insights help with investigation + +### **With Development Workflow** + +- Implementation follows development standards +- Architecture decisions drive implementation approach +- Platform requirements inform testing strategy + +## Feedback & Improvement + +### **Sub-Rule Ratings (1-5 scale)** + +- **Development Workflow**: ___/5 - Comments: _______________ +- **Type Safety**: ___/5 - Comments: _______________ +- **Architecture Patterns**: ___/5 - Comments: _______________ +- **Platform Services**: ___/5 - Comments: _______________ +- **Build Validation**: ___/5 - Comments: _______________ + +### **Workflow Feedback** + +- **Implementation Clarity**: How clear was the implementation guidance? +- **Pattern Effectiveness**: How well did architecture patterns work? +- **Platform Coverage**: How well did platform guidance cover your needs? + +### **Sub-Rule Improvements** + +- **Clarity Issues**: Which rules were unclear or confusing? +- **Missing Examples**: What examples would make rules more useful? +- **Integration Problems**: Do any rules conflict or overlap? + +### **Overall Experience** + +- **Time Saved**: How much time did this meta-rule save you? +- **Quality Improvement**: Did following these rules improve your implementation? +- **Recommendation**: Would you recommend this meta-rule to others? + +## Model Implementation Checklist + +### Before Feature Implementation + +- [ ] **Planning Review**: Review feature planning and requirements +- [ ] **Architecture Planning**: Plan architecture and design patterns +- [ ] **Platform Analysis**: Understand platform-specific requirements +- [ ] **Testing Strategy**: Plan testing approach for the feature + +### During Feature Implementation + +- [ ] **Rule Application**: Apply bundled rules in recommended sequence +- [ ] **Implementation**: Implement feature following development standards +- [ ] **Testing**: Test feature across all target platforms +- [ ] **Documentation**: Document implementation details and decisions + +### After Feature Implementation + +- [ ] **Validation**: Verify feature meets all success criteria +- [ ] **Code Review**: Complete code review with team +- [ ] **Testing**: Complete comprehensive testing across platforms +- [ ] **Feedback Collection**: Collect feedback on meta-rule effectiveness + +--- + +**See also**: + +- `.cursor/rules/meta_feature_planning.mdc` for planning workflow +- `.cursor/rules/meta_bug_fixing.mdc` for fix implementation patterns +- `.cursor/rules/meta_bug_diagnosis.mdc` for investigation insights + +**Status**: Active meta-rule for feature implementation +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: Development team, Architecture team, QA team diff --git a/.cursor/rules/meta_feature_planning.mdc b/.cursor/rules/meta_feature_planning.mdc new file mode 100644 index 00000000..f76b09b6 --- /dev/null +++ b/.cursor/rules/meta_feature_planning.mdc @@ -0,0 +1,203 @@ +# Meta-Rule: Feature Planning + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Feature planning workflow bundling + +## Purpose + +This meta-rule bundles all the rules needed for comprehensive feature planning +across all platforms. Use this when starting any new feature development, +planning sprints, or estimating work effort. + +## Workflow Constraints + +**This meta-rule enforces PLANNING MODE for all bundled sub-rules:** + +```json +{ + "workflowMode": "planning", + "constraints": { + "mode": "design_only", + "allowed": ["analyze", "plan", "design", "estimate", "document"], + "forbidden": ["implement", "code", "build", "test", "deploy"] + } +} +``` + +**All bundled sub-rules automatically inherit these constraints.** + +## Workflow State Update + +**When this meta-rule is invoked, update the workflow state file:** + +```json +{ + "currentMode": "planning", + "lastInvoked": "meta_feature_planning.mdc", + "timestamp": "2025-01-27T15:30:00Z", + "constraints": { + "mode": "design_only", + "allowed": ["analyze", "plan", "design", "estimate", "document"], + "forbidden": ["implement", "code", "build", "test", "deploy"] + } +} +``` + +**State File Location**: `.cursor/rules/.workflow_state.json` + +**This enables the core always-on rule to enforce planning mode constraints.** + +## When to Use + +- **New Feature Development**: Planning features from concept to implementation +- **Sprint Planning**: Estimating effort and breaking down work +- **Architecture Decisions**: Planning major architectural changes +- **Platform Expansion**: Adding features to new platforms +- **Refactoring Planning**: Planning significant code restructuring + +## Bundled Rules + +### **Core Planning Foundation** + +- **`development/planning_examples.mdc`** - Planning templates, examples, and + best practices for structured planning +- **`development/realistic_time_estimation.mdc`** - Time estimation framework + with complexity-based phases and milestones +- **`development/complexity_assessment.mdc`** - Technical and business + complexity evaluation with risk assessment + +### **Platform & Architecture** + +- **`app/timesafari_platforms.mdc`** - Platform-specific requirements, + constraints, and capabilities across web/mobile/desktop +- **`app/architectural_decision_record.mdc`** - ADR process for documenting + major architectural decisions and trade-offs + +### **Development Context** + +- **`app/timesafari.mdc`** - Core application context, principles, and + development focus areas +- **`app/timesafari_development.mdc`** - TimeSafari-specific development + workflow and quality standards + +## Workflow Sequence + +### **Phase 1: Foundation (Start Here)** + +1. **Complexity Assessment** - Use `complexity_assessment.mdc` to evaluate + technical and business complexity +2. **Time Estimation** - Apply `realistic_time_estimation.mdc` framework + based on complexity results +3. **Core Planning** - Use `planning_examples.mdc` for structured planning + approach + +### **Phase 2: Platform & Architecture** + +1. **Platform Analysis** - Review `timesafari_platforms.mdc` for + platform-specific requirements +2. **Architecture Planning** - Use `architectural_decision_record.mdc` if + major architectural changes are needed + +### **Phase 3: Implementation Planning** + +1. **Development Workflow** - Reference `timesafari_development.mdc` for + development standards and testing strategy +2. **Final Planning** - Consolidate all inputs into comprehensive plan + +## Success Criteria + +- [ ] **Complexity assessed** and documented with risk factors +- [ ] **Time estimate created** with clear phases and milestones +- [ ] **Platform requirements identified** for all target platforms +- [ ] **Architecture decisions documented** (if major changes needed) +- [ ] **Testing strategy planned** with platform-specific considerations +- [ ] **Dependencies mapped** between tasks and phases +- [ ] **Stakeholder input gathered** and incorporated + +## Common Pitfalls + +- **Don't skip complexity assessment** - leads to unrealistic estimates +- **Don't estimate without platform analysis** - misses platform-specific work +- **Don't plan without stakeholder input** - creates misaligned expectations +- **Don't ignore testing strategy** - leads to incomplete planning +- **Don't skip architecture decisions** - creates technical debt + +## Integration Points + +### **With Other Meta-Rules** + +- **Bug Diagnosis**: Use complexity assessment for bug investigation planning +- **Feature Implementation**: This planning feeds directly into implementation +- **Code Review**: Planning standards inform review requirements + +### **With Development Workflow** + +- Planning outputs become inputs for sprint planning +- Complexity assessment informs testing strategy +- Platform requirements drive architecture decisions + +## Feedback & Improvement + +### **Sub-Rule Ratings (1-5 scale)** + +- **Complexity Assessment**: ___/5 - Comments: _______________ +- **Time Estimation**: ___/5 - Comments: _______________ +- **Planning Examples**: ___/5 - Comments: _______________ +- **Platform Analysis**: ___/5 - Comments: _______________ +- **Architecture Decisions**: ___/5 - Comments: _______________ + +### **Workflow Feedback** + +- **Sequence Effectiveness**: Did the recommended order work for you? +- **Missing Guidance**: What additional information would have helped? +- **Process Gaps**: Where did the workflow break down? + +### **Sub-Rule Improvements** + +- **Clarity Issues**: Which rules were unclear or confusing? +- **Missing Examples**: What examples would make rules more useful? +- **Integration Problems**: Do any rules conflict or overlap? + +### **Overall Experience** + +- **Time Saved**: How much time did this meta-rule save you? +- **Quality Improvement**: Did following these rules improve your planning? +- **Recommendation**: Would you recommend this meta-rule to others? + +## Model Implementation Checklist + +### Before Feature Planning + +- [ ] **Scope Definition**: Clearly define the feature scope and boundaries +- [ ] **Stakeholder Identification**: Identify all stakeholders and decision makers +- [ ] **Platform Requirements**: Understand target platforms and constraints +- [ ] **Complexity Assessment**: Plan complexity evaluation approach + +### During Feature Planning + +- [ ] **Rule Application**: Apply bundled rules in recommended sequence +- [ ] **Documentation**: Document all planning decisions and rationale +- [ ] **Stakeholder Input**: Gather and incorporate stakeholder feedback +- [ ] **Validation**: Validate planning against success criteria + +### After Feature Planning + +- [ ] **Plan Review**: Review plan with stakeholders and team +- [ ] **Feedback Collection**: Collect feedback on meta-rule effectiveness +- [ ] **Documentation Update**: Update relevant documentation +- [ ] **Process Improvement**: Identify improvements for future planning + +--- + +**See also**: + +- `.cursor/rules/meta_bug_diagnosis.mdc` for investigation planning +- `.cursor/rules/meta_feature_implementation.mdc` for implementation workflow +- `.cursor/rules/meta_bug_fixing.mdc` for fix implementation + +**Status**: Active meta-rule for feature planning +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: Development team, Product team, Architecture team diff --git a/.cursor/rules/meta_research.mdc b/.cursor/rules/meta_research.mdc new file mode 100644 index 00000000..0bbd0fbe --- /dev/null +++ b/.cursor/rules/meta_research.mdc @@ -0,0 +1,285 @@ +# Meta-Rule: Enhanced Research Workflows + +**Author**: Matthew Raymer +**Date**: 2025-01-27 +**Status**: 🎯 **ACTIVE** - Research and investigation workflows + +## Purpose + +This meta-rule bundles research-specific rules that should be applied when conducting +systematic investigation, analysis, evidence collection, or research tasks. It provides +a comprehensive framework for thorough, methodical research workflows that produce +actionable insights and evidence-based conclusions. + +## Workflow Constraints + +**This meta-rule enforces RESEARCH MODE for all bundled sub-rules:** + +```json +{ + "workflowMode": "research", + "constraints": { + "mode": "investigation", + "allowed": ["read", "search", "analyze", "plan"], + "forbidden": ["modify", "create", "build", "commit"] + } +} +``` + +**All bundled sub-rules automatically inherit these constraints.** + +## Workflow State Update + +**When this meta-rule is invoked, update the workflow state file:** + +```json +{ + "currentMode": "research", + "lastInvoked": "meta_research.mdc", + "timestamp": "2025-01-27T15:30:00Z", + "constraints": { + "mode": "investigation", + "allowed": ["read", "search", "analyze", "plan"], + "forbidden": ["modify", "create", "build", "commit"] + } +} +``` + +**State File Location**: `.cursor/rules/.workflow_state.json` + +**This enables the core always-on rule to enforce research mode constraints.** + +## When to Use + +**RESEARCH TASKS** - Apply this meta-rule when: + +- Investigating bugs, defects, or system issues +- Conducting technical research or feasibility analysis +- Analyzing codebases, architectures, or dependencies +- Researching solutions, alternatives, or best practices +- Collecting evidence for decision-making or documentation +- Performing root cause analysis or impact assessment + +## Bundled Rules + +### **Core Research Principles** + +- **`development/research_diagnostic.mdc`** - Systematic investigation workflow + and evidence collection methodology +- **`development/type_safety_guide.mdc`** - Type analysis and safety research + for TypeScript/JavaScript codebases + +### **Investigation & Analysis** + +- **`workflow/version_control.mdc`** - Git history analysis and commit research +- **`workflow/commit_messages.mdc`** - Commit pattern analysis and history + investigation + +### **Platform & Context Research** + +- **`app/timesafari.mdc`** - Application context research and platform + understanding +- **`app/timesafari_platforms.mdc`** - Platform-specific research and + capability analysis + +## Why These Rules Are Research-Focused + +### **Research Diagnostic** + +- **Systematic Approach**: Provides structured investigation methodology +- **Evidence Collection**: Ensures thorough data gathering and documentation +- **Root Cause Analysis**: Guides systematic problem investigation +- **Impact Assessment**: Helps evaluate scope and consequences + +### **Type Safety Research** + +- **Code Analysis**: Enables systematic type system investigation +- **Safety Assessment**: Guides research into type-related issues +- **Migration Planning**: Supports research for architectural changes + +### **Version Control Research** + +- **History Analysis**: Enables investigation of code evolution +- **Pattern Recognition**: Helps identify commit and change patterns +- **Timeline Research**: Supports chronological investigation + +### **Platform Research** + +- **Capability Analysis**: Guides research into platform-specific features +- **Context Understanding**: Ensures research considers application context +- **Cross-Platform Research**: Supports multi-platform investigation + +## Application Priority + +### **Primary (Apply First)** + +1. **Research Diagnostic** - Systematic investigation methodology +2. **Type Safety Guide** - Code analysis and type research +3. **Application Context** - Platform and context understanding + +### **Secondary (Apply as Needed)** + +1. **Version Control** - When investigating code history +2. **Platform Details** - When researching platform-specific capabilities + +## Integration with Other Meta-Rules + +### **Bug Diagnosis** + +- Research meta-rule provides investigation methodology +- Core always-on ensures systematic approach +- Application context provides system understanding + +### **Feature Planning** + +- Research meta-rule guides feasibility research +- Core always-on ensures competence focus +- Application context drives platform considerations + +### **Architecture Analysis** + +- Research meta-rule provides systematic analysis framework +- Core always-on ensures quality standards +- Application context informs architectural decisions + +### **Performance Investigation** + +- Research meta-rule guides systematic performance research +- Core always-on ensures thorough investigation +- Application context provides performance context + +## Research Workflow Phases + +### **Phase 1: Investigation Setup** + +1. **Scope Definition** - Define research boundaries and objectives +2. **Context Gathering** - Collect relevant application and platform context +3. **Methodology Selection** - Choose appropriate research approaches + +### **Phase 2: Evidence Collection** + +1. **Systematic Data Gathering** - Collect evidence using structured methods +2. **Documentation** - Record all findings and observations +3. **Validation** - Verify evidence accuracy and relevance + +### **Phase 3: Analysis & Synthesis** + +1. **Pattern Recognition** - Identify trends and patterns in evidence +2. **Root Cause Analysis** - Determine underlying causes and factors +3. **Impact Assessment** - Evaluate scope and consequences + +### **Phase 4: Conclusion & Action** + +1. **Evidence-Based Conclusions** - Draw conclusions from collected evidence +2. **Actionable Recommendations** - Provide specific, implementable guidance +3. **Documentation** - Create comprehensive research documentation + +## Success Criteria + +- [ ] **Research diagnostic applied** to all investigation tasks +- [ ] **Type safety research** conducted for code analysis +- [ ] **Evidence collection** systematic and comprehensive +- [ ] **Root cause analysis** thorough and accurate +- [ ] **Conclusions actionable** and evidence-based +- [ ] **Documentation complete** and searchable + +## Common Research Pitfalls + +- **Don't skip systematic approach** - leads to incomplete investigation +- **Don't ignore evidence validation** - creates unreliable conclusions +- **Don't forget context** - misses important factors +- **Don't skip documentation** - loses research value +- **Don't rush conclusions** - produces poor recommendations + +## Research Quality Standards + +### **Evidence Quality** + +- **Completeness**: All relevant evidence collected +- **Accuracy**: Evidence verified and validated +- **Relevance**: Evidence directly addresses research questions +- **Timeliness**: Evidence current and up-to-date + +### **Analysis Quality** + +- **Systematic**: Analysis follows structured methodology +- **Objective**: Analysis free from bias and assumptions +- **Thorough**: All evidence considered and evaluated +- **Logical**: Conclusions follow from evidence + +### **Documentation Quality** + +- **Comprehensive**: All findings and methods documented +- **Searchable**: Documentation easily findable and navigable +- **Actionable**: Recommendations specific and implementable +- **Maintainable**: Documentation structure supports updates + +## Feedback & Improvement + +### **Rule Effectiveness Ratings (1-5 scale)** + +- **Research Diagnostic**: ___/5 - Comments: _______________ +- **Type Safety Guide**: ___/5 - Comments: _______________ +- **Version Control**: ___/5 - Comments: _______________ +- **Platform Context**: ___/5 - Comments: _______________ + +### **Research Workflow Effectiveness** + +- **Investigation Quality**: Are research tasks producing thorough results? +- **Evidence Collection**: Is evidence gathering systematic and complete? +- **Conclusion Quality**: Are conclusions actionable and evidence-based? +- **Documentation Value**: Is research documentation useful and maintainable? + +### **Integration Feedback** + +- **With Other Meta-Rules**: How well does this integrate with workflow rules? +- **Context Switching**: Do these rules help or hinder research context? +- **Learning Curve**: Are these rules easy for new researchers to understand? + +### **Overall Research Experience** + +- **Quality Improvement**: Do these rules improve research outcomes? +- **Efficiency**: Do these rules make research more efficient? +- **Recommendation**: Would you recommend keeping this research meta-rule? + +## Model Implementation Checklist + +### Before Research Tasks + +- [ ] **Research Diagnostic**: Ensure systematic investigation methodology +- [ ] **Type Safety Guide**: Prepare for code analysis if needed +- [ ] **Application Context**: Load relevant platform and context information +- [ ] **Version Control**: Prepare for history analysis if needed + +### During Research Execution + +- [ ] **Systematic Approach**: Follow structured investigation methodology +- [ ] **Evidence Collection**: Gather comprehensive and validated evidence +- [ ] **Documentation**: Record all findings and observations +- [ ] **Context Awareness**: Consider application and platform context + +### After Research Completion + +- [ ] **Validation**: Verify all research phases completed +- [ ] **Quality Check**: Ensure research meets quality standards +- [ ] **Documentation Review**: Confirm research properly documented +- [ ] **Feedback Collection**: Note any issues with research process + +--- + +**See also**: + +- `.cursor/rules/meta_core_always_on.mdc` for core always-on rules +- `.cursor/rules/meta_feature_planning.mdc` for feature development workflows +- `.cursor/rules/meta_bug_diagnosis.mdc` for bug investigation workflows +- `.cursor/rules/meta_bug_fixing.mdc` for fix implementation workflows + +**Status**: Active research meta-rule +**Priority**: High (applies to all research tasks) +**Estimated Effort**: Ongoing reference +**Dependencies**: All bundled sub-rules +**Stakeholders**: Development team, Research team, Quality Assurance team +description: +globs: +alwaysApply: false +--- diff --git a/.cursor/rules/meta_rule_architecture.md b/.cursor/rules/meta_rule_architecture.md new file mode 100644 index 00000000..9f0bfb0b --- /dev/null +++ b/.cursor/rules/meta_rule_architecture.md @@ -0,0 +1,103 @@ +# Meta-Rule Architecture Overview + +**Author**: Matthew Raymer +**Date**: 2025-01-27 +**Status**: 📋 **ACTIVE** - Meta-rule organization and relationships + +## Meta-Rule Structure + +### **Core Always-On Rules** (`meta_core_always_on.mdc`) +- **Purpose**: Applied to every single prompt +- **Scope**: Human competence, time standards, version control, application context +- **Priority**: Critical - foundation for all interactions + +### **Enhanced Research Workflows** (`meta_research.mdc`) ⭐ **NEW** +- **Purpose**: Applied to research, investigation, and analysis tasks +- **Scope**: Systematic investigation, evidence collection, root cause analysis +- **Priority**: High - applies to all research tasks +- **Bundles**: Research diagnostic, type safety, version control research, platform context + +### **Feature Development Workflows** (`meta_feature_planning.mdc`) +- **Purpose**: Applied to feature planning and development tasks +- **Scope**: Requirements analysis, architecture planning, implementation strategy +- **Priority**: High - applies to feature development + +### **Bug Investigation Workflows** (`meta_bug_diagnosis.mdc`) +- **Purpose**: Applied to bug investigation and diagnosis tasks +- **Scope**: Defect analysis, evidence collection, root cause identification +- **Priority**: High - applies to bug investigation + +### **Bug Fixing Workflows** (`meta_bug_fixing.mdc`) +- **Purpose**: Applied to bug fixing and resolution tasks +- **Scope**: Fix implementation, testing, validation +- **Priority**: High - applies to bug resolution + +## Research Meta-Rule Integration + +### **When to Use Research Meta-Rule** + +The research meta-rule should be applied when: +- **Investigating bugs** - systematic defect analysis +- **Researching solutions** - feasibility and alternative analysis +- **Analyzing codebases** - architecture and dependency research +- **Collecting evidence** - systematic data gathering +- **Root cause analysis** - systematic problem investigation +- **Impact assessment** - scope and consequence evaluation + +### **How It Complements Other Meta-Rules** + +- **Core Always-On**: Provides foundation (competence, time, context) +- **Research**: Adds systematic investigation methodology +- **Feature Planning**: Guides feasibility research and analysis +- **Bug Diagnosis**: Provides investigation framework +- **Bug Fixing**: Informs fix strategy through research + +### **Research Workflow Phases** + +1. **Investigation Setup** - Scope, context, methodology +2. **Evidence Collection** - Systematic data gathering +3. **Analysis & Synthesis** - Pattern recognition, root cause +4. **Conclusion & Action** - Evidence-based recommendations + +## Usage Examples + +### **Bug Investigation** +``` +Apply: meta_core_always_on + meta_research + meta_bug_diagnosis +Result: Systematic investigation with evidence collection and root cause analysis +``` + +### **Feature Research** +``` +Apply: meta_core_always_on + meta_research + meta_feature_planning +Result: Comprehensive feasibility research with platform context +``` + +### **Architecture Analysis** +``` +Apply: meta_core_always_on + meta_research +Result: Systematic architecture investigation with evidence-based conclusions +``` + +## Benefits of Enhanced Research Meta-Rule + +- **Systematic Approach**: Structured investigation methodology +- **Evidence-Based**: Comprehensive data collection and validation +- **Quality Standards**: Defined research quality criteria +- **Integration**: Seamless integration with existing workflows +- **Documentation**: Comprehensive research documentation standards + +## Next Steps + +1. **Test Research Meta-Rule** - Apply to next research task +2. **Validate Integration** - Ensure smooth workflow integration +3. **Collect Feedback** - Gather effectiveness ratings +4. **Iterate** - Refine based on usage experience + +--- + +**Status**: Active documentation +**Priority**: Medium +**Estimated Effort**: Ongoing reference +**Dependencies**: All meta-rules +**Stakeholders**: Development team, Research team diff --git a/.cursor/rules/playwright-test-investigation.mdc b/.cursor/rules/playwright-test-investigation.mdc new file mode 100644 index 00000000..c7a97c9d --- /dev/null +++ b/.cursor/rules/playwright-test-investigation.mdc @@ -0,0 +1,356 @@ +--- +description: when working with playwright tests either generating them or using them to test code +alwaysApply: false +--- +# Playwright Test Investigation — Harbor Pilot Directive + +**Author**: Matthew Raymer +**Date**: 2025-08-21T14:22Z +**Status**: 🎯 **ACTIVE** - Playwright test debugging guidelines + +## Objective +Provide systematic approach for investigating Playwright test failures with focus on UI element conflicts, timing issues, and selector ambiguity. + +## Context & Scope +- **Audience**: Developers debugging Playwright test failures +- **In scope**: Test failure analysis, selector conflicts, UI state investigation, timing issues +- **Out of scope**: Test writing best practices, CI/CD configuration + +## Artifacts & Links +- Test results: `test-results/` directory +- Error context: `error-context.md` files with page snapshots +- Trace files: `trace.zip` files for failed tests +- HTML reports: Interactive test reports with screenshots + +## Environment & Preconditions +- OS/Runtime: Linux/Windows/macOS with Node.js +- Versions: Playwright test framework, browser drivers +- Services: Local test server (localhost:8080), test data setup +- Auth mode: None required for test investigation + +## Architecture / Process Overview +Playwright test investigation follows a systematic diagnostic workflow that leverages built-in debugging tools and error context analysis. + +```mermaid +flowchart TD + A[Test Failure] --> B[Check Error Context] + B --> C[Analyze Page Snapshot] + C --> D[Identify UI Conflicts] + D --> E[Check Trace Files] + E --> F[Verify Selector Uniqueness] + F --> G[Test Selector Fixes] + G --> H[Document Root Cause] + + B --> I[Check Test Results Directory] + I --> J[Locate Failed Test Results] + J --> K[Extract Error Details] + + D --> L[Multiple Alerts?] + L --> M[Button Text Conflicts?] + M --> N[Timing Issues?] + + E --> O[Use Trace Viewer] + O --> P[Analyze Action Sequence] + P --> Q[Identify Failure Point] +``` + +## Interfaces & Contracts + +### Test Results Structure +| Component | Format | Content | Validation | +|---|---|---|---| +| Error Context | Markdown | Page snapshot in YAML | Verify DOM state matches test expectations | +| Trace Files | ZIP archive | Detailed execution trace | Use `npx playwright show-trace` | +| HTML Reports | Interactive HTML | Screenshots, traces, logs | Check browser for full report | +| JSON Results | JSON | Machine-readable results | Parse for automated analysis | + +### Investigation Commands +| Step | Command | Expected Output | Notes | +|---|---|---|---| +| Locate failed tests | `find test-results -name "*test-name*"` | Test result directories | Use exact test name patterns | +| Check error context | `cat test-results/*/error-context.md` | Page snapshots | Look for UI state conflicts | +| View traces | `npx playwright show-trace trace.zip` | Interactive trace viewer | Analyze exact failure sequence | + +## Repro: End-to-End Investigation Procedure + +### 1. Locate Failed Test Results +```bash +# Find all results for a specific test +find test-results -name "*test-name*" -type d + +# Check for error context files +find test-results -name "error-context.md" | head -5 +``` + +### 2. Analyze Error Context +```bash +# Read error context for specific test +cat test-results/test-name-test-description-browser/error-context.md + +# Look for UI conflicts in page snapshot +grep -A 10 -B 5 "button.*Yes\|button.*No" test-results/*/error-context.md +``` + +### 3. Check Trace Files +```bash +# List available trace files +find test-results -name "*.zip" | grep trace + +# View trace in browser +npx playwright show-trace test-results/test-name/trace.zip +``` + +### 4. Investigate Selector Issues +```typescript +// Check for multiple elements with same text +await page.locator('button:has-text("Yes")').count(); // Should be 1 + +// Use more specific selectors +await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes")').click(); +``` + +## What Works (Evidence) +- ✅ **Error context files** provide page snapshots showing exact DOM state at failure + - **Time**: 2025-08-21T14:22Z + - **Evidence**: `test-results/60-new-activity-New-offers-for-another-user-chromium/error-context.md` shows both alerts visible + - **Verify at**: Error context files in test results directory + +- ✅ **Trace files** capture detailed execution sequence for failed tests + - **Time**: 2025-08-21T14:22Z + - **Evidence**: `trace.zip` files available for all failed tests + - **Verify at**: Use `npx playwright show-trace <filename>` + +- ✅ **Page snapshots** reveal UI conflicts like multiple alerts with duplicate button text + - **Time**: 2025-08-21T14:22Z + - **Evidence**: YAML snapshots show registration + export alerts simultaneously + - **Verify at**: Error context markdown files + +## What Doesn't (Evidence & Hypotheses) +- ❌ **Generic selectors** fail with multiple similar elements at `test-playwright/testUtils.ts:161` + - **Time**: 2025-08-21T14:22Z + - **Evidence**: `button:has-text("Yes")` matches both "Yes" and "Yes, Export Data" + - **Hypothesis**: Selector ambiguity due to multiple alerts with conflicting button text + - **Next probe**: Use more specific selectors or dismiss alerts sequentially + +- ❌ **Timing-dependent tests** fail due to alert stacking at `src/views/ContactsView.vue:860,1283` + - **Time**: 2025-08-21T14:22Z + - **Evidence**: Both alerts use identical 1000ms delays, ensuring simultaneous display + - **Hypothesis**: Race condition between alert displays creates UI conflicts + - **Next probe**: Implement alert queuing or prevent overlapping alerts + +## Risks, Limits, Assumptions +- **Trace file size**: Large trace files may impact storage and analysis time +- **Browser compatibility**: Trace viewer requires specific browser support +- **Test isolation**: Shared state between tests may affect investigation results +- **Timing sensitivity**: Tests may pass/fail based on system performance + +## Next Steps +| Owner | Task | Exit Criteria | Target Date (UTC) | +|---|---|---|---| +| Development Team | Fix test selectors for multiple alerts | All tests pass consistently | 2025-08-22 | +| Development Team | Implement alert queuing system | No overlapping alerts with conflicting buttons | 2025-08-25 | +| Development Team | Add test IDs to alert buttons | Unique selectors for all UI elements | 2025-08-28 | + +## References +- [Playwright Trace Viewer Documentation](https://playwright.dev/docs/trace-viewer) +- [Playwright Test Results](https://playwright.dev/docs/test-reporters) +- [Test Investigation Workflow](./research_diagnostic.mdc) + +## Competence Hooks +- **Why this works**: Systematic investigation leverages Playwright's built-in debugging tools to identify root causes +- **Common pitfalls**: Generic selectors fail with multiple similar elements; timing issues create race conditions; alert stacking causes UI conflicts +- **Next skill unlock**: Implement unique test IDs and handle alert dismissal order in test flows +- **Teach-back**: "How would you investigate a Playwright test failure using error context, trace files, and page snapshots?" + +## Collaboration Hooks +- **Reviewers**: QA team, test automation engineers +- **Sign-off checklist**: Error context analyzed, trace files reviewed, root cause identified, fix implemented and tested + +## Assumptions & Limits +- Test results directory structure follows Playwright conventions +- Trace files are enabled in configuration (`trace: "retain-on-failure"`) +- Error context files contain valid YAML page snapshots +- Browser environment supports trace viewer functionality + +--- + +**Status**: Active investigation directive +**Priority**: High +**Maintainer**: Development team +**Next Review**: 2025-09-21 +# Playwright Test Investigation — Harbor Pilot Directive + +**Author**: Matthew Raymer +**Date**: 2025-08-21T14:22Z +**Status**: 🎯 **ACTIVE** - Playwright test debugging guidelines + +## Objective +Provide systematic approach for investigating Playwright test failures with focus on UI element conflicts, timing issues, and selector ambiguity. + +## Context & Scope +- **Audience**: Developers debugging Playwright test failures +- **In scope**: Test failure analysis, selector conflicts, UI state investigation, timing issues +- **Out of scope**: Test writing best practices, CI/CD configuration + +## Artifacts & Links +- Test results: `test-results/` directory +- Error context: `error-context.md` files with page snapshots +- Trace files: `trace.zip` files for failed tests +- HTML reports: Interactive test reports with screenshots + +## Environment & Preconditions +- OS/Runtime: Linux/Windows/macOS with Node.js +- Versions: Playwright test framework, browser drivers +- Services: Local test server (localhost:8080), test data setup +- Auth mode: None required for test investigation + +## Architecture / Process Overview +Playwright test investigation follows a systematic diagnostic workflow that leverages built-in debugging tools and error context analysis. + +```mermaid +flowchart TD + A[Test Failure] --> B[Check Error Context] + B --> C[Analyze Page Snapshot] + C --> D[Identify UI Conflicts] + D --> E[Check Trace Files] + E --> F[Verify Selector Uniqueness] + F --> G[Test Selector Fixes] + G --> H[Document Root Cause] + + B --> I[Check Test Results Directory] + I --> J[Locate Failed Test Results] + J --> K[Extract Error Details] + + D --> L[Multiple Alerts?] + L --> M[Button Text Conflicts?] + M --> N[Timing Issues?] + + E --> O[Use Trace Viewer] + O --> P[Analyze Action Sequence] + P --> Q[Identify Failure Point] +``` + +## Interfaces & Contracts + +### Test Results Structure +| Component | Format | Content | Validation | +|---|---|---|---| +| Error Context | Markdown | Page snapshot in YAML | Verify DOM state matches test expectations | +| Trace Files | ZIP archive | Detailed execution trace | Use `npx playwright show-trace` | +| HTML Reports | Interactive HTML | Screenshots, traces, logs | Check browser for full report | +| JSON Results | JSON | Machine-readable results | Parse for automated analysis | + +### Investigation Commands +| Step | Command | Expected Output | Notes | +|---|---|---|---| +| Locate failed tests | `find test-results -name "*test-name*"` | Test result directories | Use exact test name patterns | +| Check error context | `cat test-results/*/error-context.md` | Page snapshots | Look for UI state conflicts | +| View traces | `npx playwright show-trace trace.zip` | Interactive trace viewer | Analyze exact failure sequence | + +## Repro: End-to-End Investigation Procedure + +### 1. Locate Failed Test Results +```bash +# Find all results for a specific test +find test-results -name "*test-name*" -type d + +# Check for error context files +find test-results -name "error-context.md" | head -5 +``` + +### 2. Analyze Error Context +```bash +# Read error context for specific test +cat test-results/test-name-test-description-browser/error-context.md + +# Look for UI conflicts in page snapshot +grep -A 10 -B 5 "button.*Yes\|button.*No" test-results/*/error-context.md +``` + +### 3. Check Trace Files +```bash +# List available trace files +find test-results -name "*.zip" | grep trace + +# View trace in browser +npx playwright show-trace test-results/test-name/trace.zip +``` + +### 4. Investigate Selector Issues +```typescript +// Check for multiple elements with same text +await page.locator('button:has-text("Yes")').count(); // Should be 1 + +// Use more specific selectors +await page.locator('div[role="alert"]:has-text("Register") button:has-text("Yes")').click(); +``` + +## What Works (Evidence) +- ✅ **Error context files** provide page snapshots showing exact DOM state at failure + - **Time**: 2025-08-21T14:22Z + - **Evidence**: `test-results/60-new-activity-New-offers-for-another-user-chromium/error-context.md` shows both alerts visible + - **Verify at**: Error context files in test results directory + +- ✅ **Trace files** capture detailed execution sequence for failed tests + - **Time**: 2025-08-21T14:22Z + - **Evidence**: `trace.zip` files available for all failed tests + - **Verify at**: Use `npx playwright show-trace <filename>` + +- ✅ **Page snapshots** reveal UI conflicts like multiple alerts with duplicate button text + - **Time**: 2025-08-21T14:22Z + - **Evidence**: YAML snapshots show registration + export alerts simultaneously + - **Verify at**: Error context markdown files + +## What Doesn't (Evidence & Hypotheses) +- ❌ **Generic selectors** fail with multiple similar elements at `test-playwright/testUtils.ts:161` + - **Time**: 2025-08-21T14:22Z + - **Evidence**: `button:has-text("Yes")` matches both "Yes" and "Yes, Export Data" + - **Hypothesis**: Selector ambiguity due to multiple alerts with conflicting button text + - **Next probe**: Use more specific selectors or dismiss alerts sequentially + +- ❌ **Timing-dependent tests** fail due to alert stacking at `src/views/ContactsView.vue:860,1283` + - **Time**: 2025-08-21T14:22Z + - **Evidence**: Both alerts use identical 1000ms delays, ensuring simultaneous display + - **Hypothesis**: Race condition between alert displays creates UI conflicts + - **Next probe**: Implement alert queuing or prevent overlapping alerts + +## Risks, Limits, Assumptions +- **Trace file size**: Large trace files may impact storage and analysis time +- **Browser compatibility**: Trace viewer requires specific browser support +- **Test isolation**: Shared state between tests may affect investigation results +- **Timing sensitivity**: Tests may pass/fail based on system performance + +## Next Steps +| Owner | Task | Exit Criteria | Target Date (UTC) | +|---|---|---|---| +| Development Team | Fix test selectors for multiple alerts | All tests pass consistently | 2025-08-22 | +| Development Team | Implement alert queuing system | No overlapping alerts with conflicting buttons | 2025-08-25 | +| Development Team | Add test IDs to alert buttons | Unique selectors for all UI elements | 2025-08-28 | + +## References +- [Playwright Trace Viewer Documentation](https://playwright.dev/docs/trace-viewer) +- [Playwright Test Results](https://playwright.dev/docs/test-reporters) +- [Test Investigation Workflow](./research_diagnostic.mdc) + +## Competence Hooks +- **Why this works**: Systematic investigation leverages Playwright's built-in debugging tools to identify root causes +- **Common pitfalls**: Generic selectors fail with multiple similar elements; timing issues create race conditions; alert stacking causes UI conflicts +- **Next skill unlock**: Implement unique test IDs and handle alert dismissal order in test flows +- **Teach-back**: "How would you investigate a Playwright test failure using error context, trace files, and page snapshots?" + +## Collaboration Hooks +- **Reviewers**: QA team, test automation engineers +- **Sign-off checklist**: Error context analyzed, trace files reviewed, root cause identified, fix implemented and tested + +## Assumptions & Limits +- Test results directory structure follows Playwright conventions +- Trace files are enabled in configuration (`trace: "retain-on-failure"`) +- Error context files contain valid YAML page snapshots +- Browser environment supports trace viewer functionality + +--- + +**Status**: Active investigation directive +**Priority**: High +**Maintainer**: Development team +**Next Review**: 2025-09-21 diff --git a/.cursor/rules/templates/adr_template.mdc b/.cursor/rules/templates/adr_template.mdc new file mode 100644 index 00000000..6114b287 --- /dev/null +++ b/.cursor/rules/templates/adr_template.mdc @@ -0,0 +1,98 @@ +--- +alwaysApply: false +--- + +# ADR Template + +## ADR-XXXX-YY-ZZ: [Short Title] + +**Date:** YYYY-MM-DD +**Status:** [PROPOSED | ACCEPTED | REJECTED | DEPRECATED | SUPERSEDED] +**Deciders:** [List of decision makers] +**Technical Story:** [Link to issue/PR if applicable] + +## Context + +[Describe the forces at play, including technological, political, social, and +project local. These forces are probably in tension, and should be called out as +such. The language in this section is value-neutral. It is simply describing + facts.] + +## Decision + +[Describe our response to these forces. We will use the past tense ( + "We will...").] + +## Consequences + +### Positive + +- [List positive consequences] + +### Negative + +- [List negative consequences or trade-offs] + +### Neutral + +- [List neutral consequences or notes] + +## Alternatives Considered + +- **Alternative 1:** [Description] - [Why rejected] + +- **Alternative 2:** [Description] - [Why rejected] + +- **Alternative 3:** [Description] - [Why rejected] + +## Implementation Notes + +[Any specific implementation details, migration steps, or + technical considerations] + +## References + +- [Link to relevant documentation] + +- [Link to related ADRs] + +- [Link to external resources] + +## Related Decisions + +- [List related ADRs or decisions] + +--- + +## Usage Guidelines + +1. **Copy this template** for new ADRs +2. **Number sequentially** (ADR-001, ADR-002, etc.) +3. **Use descriptive titles** that clearly indicate the decision +4. **Include all stakeholders** in the deciders list +5. **Link to related issues** and documentation +6. **Update status** as decisions evolve +7. **Store in** `doc/architecture-decisions/` directory + +## Model Implementation Checklist + +### Before ADR Creation + +- [ ] **Decision Context**: Understand the decision that needs to be made +- [ ] **Stakeholder Identification**: Identify all decision makers +- [ ] **Research**: Research alternatives and gather evidence +- [ ] **Template Selection**: Choose appropriate ADR template + +### During ADR Creation + +- [ ] **Context Documentation**: Document the context and forces at play +- [ ] **Decision Recording**: Record the decision and rationale +- [ ] **Consequences Analysis**: Analyze positive, negative, and neutral consequences +- [ ] **Alternatives Documentation**: Document alternatives considered + +### After ADR Creation + +- [ ] **Review**: Review ADR with stakeholders +- [ ] **Approval**: Get approval from decision makers +- [ ] **Communication**: Communicate decision to team +- [ ] **Implementation**: Plan implementation of the decision diff --git a/.cursor/rules/timesafari.mdc b/.cursor/rules/timesafari.mdc deleted file mode 100644 index c32804be..00000000 --- a/.cursor/rules/timesafari.mdc +++ /dev/null @@ -1,316 +0,0 @@ ---- -description: -globs: -alwaysApply: true ---- -# Time Safari Context - -## Project Overview - -Time Safari is an application designed to foster community building through gifts, -gratitude, and collaborative projects. The app should make it extremely easy and -intuitive for users of any age and capability to recognize contributions, build -trust networks, and organize collective action. It is built on services that -preserve privacy and data sovereignty. - -The ultimate goals of Time Safari are two-fold: - -1. **Connect** Make it easy, rewarding, and non-threatening for people to -connect with others who have similar interests, and to initiate activities -together. This helps people accomplish and learn from other individuals in -less-structured environments; moreover, it helps them discover who they want -to continue to support and with whom they want to maintain relationships. - -2. **Reveal** Widely advertise the great support and rewards that are being -given and accepted freely, especially non-monetary ones. Using visuals and text, -display the kind of impact that gifts are making in the lives of others. Also -show useful and engaging reports of project statistics and personal accomplishments. - - -## Core Approaches - -Time Safari should help everyday users build meaningful connections and organize -collective efforts by: - -1. **Recognizing Contributions**: Creating permanent, verifiable records of gifts -and contributions people give to each other and their communities. - -2. **Facilitating Collaboration**: Making it ridiculously easy for people to ask -for or propose help on projects and interests that matter to them. - -3. **Building Trust Networks**: Enabling users to maintain their network and activity -visibility. Developing reputation through verified contributions and references, -which can be selectively shown to others outside the network. - -4. **Preserving Privacy**: Ensuring personal identifiers are only shared with -explicitly authorized contacts, allowing private individuals including children -to participate safely. - -5. **Engaging Content**: Displaying people's records in compelling stories, and -highlighting those projects that are lifting people's lives long-term, both in -physical support and in emotional-spiritual-creative thriving. - - -## Technical Foundation - -This application is built on a privacy-preserving claims architecture (via -endorser.ch) with these key characteristics: - -- **Decentralized Identifiers (DIDs)**: User identities are based on public/private -key pairs stored on their devices -- **Cryptographic Verification**: All claims and confirmations are -cryptographically signed -- **User-Controlled Visibility**: Users explicitly control who can see their -identifiers and data -- **Merkle-Chained Claims**: Claims are cryptographically chained for verification -and integrity -- **Native and Web App**: Works on Capacitor (iOS, Android), Desktop (Electron -and CEFPython), and web browsers - -## User Journey - -The typical progression of usage follows these stages: - -1. **Gratitude & Recognition**: Users begin by expressing and recording gratitude -for gifts received, building a foundation of acknowledgment. - -2. **Project Proposals**: Users propose projects and ideas, reaching out to connect -with others who share similar interests. - -3. **Action Triggers**: Offers of help serve as triggers and motivations to execute -proposed projects, moving from ideas to action. - -## Context for LLM Development - -When developing new functionality for Time Safari, consider these design principles: - -1. **Accessibility First**: Features should be usable by non-technical users with -minimal learning curve. - -2. **Privacy by Design**: All features must respect user privacy and data sovereignty. - -3. **Progressive Enhancement**: Core functionality should work across all devices, -with richer experiences where supported. - -4. **Voluntary Collaboration**: The system should enable but never coerce participation. - -5. **Trust Building**: Features should help build verifiable trust between users. - -6. **Network Effects**: Consider how features scale as more users join the platform. - -7. **Low Resource Requirements**: The system should be lightweight enough to run -on inexpensive devices users already own. - -## Use Cases to Support - -LLM development should focus on enhancing these key use cases: - -1. **Community Building**: Tools that help people find others with shared -interests and values. - -2. **Project Coordination**: Features that make it easy to propose collaborative -projects and to submit suggestions and offers to existing ones. - -3. **Reputation Building**: Methods for users to showcase their contributions -and reliability, in contexts where they explicitly reveal that information. - -4. **Governance Experimentation**: Features that facilitate decision-making and -collective governance. - -## Constraints - -When developing new features, be mindful of these constraints: - -1. **Privacy Preservation**: User identifiers must remain private except when -explicitly shared. - -2. **Platform Limitations**: Features must work within the constraints of the target -app platforms, while aiming to leverage the best platform technology available. - -3. **Endorser API Limitations**: Backend features are constrained by the endorser.ch -API capabilities. - -4. **Performance on Low-End Devices**: The application should remain performant -on older/simpler devices. - -5. **Offline-First When Possible**: Key functionality should work offline when feasible. - -## Project Technologies - -- Typescript using ES6 classes using vue-facing-decorator -- TailwindCSS -- Vite Build Tool -- Playwright E2E testing -- IndexDB -- Camera, Image uploads, QR Code reader, ... - -## Mobile Features - -- Deep Linking -- Local Notifications via a custom Capacitor plugin - -## Project Architecture - -- The application must work on web browser, PWA (Progressive Web Application), - desktop via Electron, and mobile via Capacitor -- Building for each platform is managed via Vite - -## Core Development Principles - -### DRY development - -- **Code Reuse** - - Extract common functionality into utility functions - - Create reusable components for UI patterns - - Implement service classes for shared business logic - - Use mixins for cross-cutting concerns - - Leverage TypeScript interfaces for shared type definitions - -- **Component Patterns** - - Create base components for common UI elements - - Implement higher-order components for shared behavior - - Use slot patterns for flexible component composition - - Create composable services for business logic - - Implement factory patterns for component creation - -- **State Management** - - Centralize state in Pinia stores - - Use computed properties for derived state - - Implement shared state selectors - - Create reusable state mutations - - Use action creators for common operations - -- **Error Handling** - - Implement centralized error handling - - Create reusable error components - - Use error boundary components - - Implement consistent error logging - - Create error type definitions - -- **Type Definitions** - - Create shared interfaces for common data structures - - Use type aliases for complex types - - Implement generic types for reusable components - - Create utility types for common patterns - - Use discriminated unions for state management - -- **API Integration** - - Create reusable API client classes - - Implement request/response interceptors - - Use consistent error handling patterns - - Create type-safe API endpoints - - Implement caching strategies - -- **Platform Services** - - Abstract platform-specific code behind interfaces - - Create platform-agnostic service layers - - Implement feature detection - - Use dependency injection for services - - Create service factories - -- **Testing** - - Create reusable test utilities - - Implement test factories - - Use shared test configurations - - Create reusable test helpers - - Implement consistent test patterns - - F.I.R.S.T. (for Unit Tests) - F – Fast - I – Independent - R – Repeatable - S – Self-validating - T – Timely - -### SOLID Principles - -- **Single Responsibility**: Each class/component should have only one reason to - change - - Components should focus on one specific feature (e.g., QR scanning, DID management) - - Services should handle one type of functionality (e.g., platform services, - crypto services) - - Utilities should provide focused helper functions - -- **Open/Closed**: Software entities should be open for extension but closed for - modification - - Use interfaces for service definitions - - Implement plugin architecture for platform-specific features - - Allow component behavior extension through props and events - -- **Liskov Substitution**: Objects should be replaceable with their subtypes - - Platform services should work consistently across web/mobile - - Authentication providers should be interchangeable - - Storage implementations should be swappable - -- **Interface Segregation**: Clients shouldn't depend on interfaces they don't use - - Break down large service interfaces into smaller, focused ones - - Component props should be minimal and purposeful - - Event emissions should be specific and targeted - -- **Dependency Inversion**: High-level modules shouldn't depend on low-level modules - - Use dependency injection for services - - Abstract platform-specific code behind interfaces - - Implement factory patterns for component creation - -### Law of Demeter - -- Components should only communicate with immediate dependencies -- Avoid chaining method calls (e.g., `this.service.getUser().getProfile().getName()`) -- Use mediator patterns for complex component interactions -- Implement facade patterns for subsystem access -- Keep component communication through defined events and props - -### Composition over Inheritance - -- Prefer building components through composition -- Use mixins for shared functionality -- Implement feature toggles through props -- Create higher-order components for common patterns -- Use service composition for complex features - -### Interface Segregation - -- Define clear interfaces for services -- Keep component APIs minimal and focused -- Split large interfaces into smaller, specific ones -- Use TypeScript interfaces for type definitions -- Implement role-based interfaces for different use cases - -### Fail Fast - -- Validate inputs early in the process -- Use TypeScript strict mode -- Implement comprehensive error handling -- Add runtime checks for critical operations -- Use assertions for development-time validation - -### Principle of Least Astonishment - -- Follow Vue.js conventions consistently -- Use familiar naming patterns -- Implement predictable component behaviors -- Maintain consistent error handling -- Keep UI interactions intuitive - -### Information Hiding - -- Encapsulate implementation details -- Use private class members -- Implement proper access modifiers -- Hide complex logic behind simple interfaces -- Use TypeScript's access modifiers effectively - -### Single Source of Truth - -- Use Pinia for state management -- Maintain one source for user data -- Centralize configuration management -- Use computed properties for derived state -- Implement proper state synchronization - -### Principle of Least Privilege - -- Implement proper access control -- Use minimal required permissions -- Follow privacy-by-design principles -- Restrict component access to necessary data -- Implement proper authentication/authorization diff --git a/.cursor/rules/version_control.mdc b/.cursor/rules/version_control.mdc deleted file mode 100644 index 7635fb6b..00000000 --- a/.cursor/rules/version_control.mdc +++ /dev/null @@ -1,122 +0,0 @@ ---- -alwaysApply: true ---- -# Directive: Peaceful Co-Existence with Developers - -## 1) Version-Control Ownership - -* **MUST NOT** run `git add`, `git commit`, or any write action. -* **MUST** leave staging/committing to the developer. - -## 2) Source of Truth for Commit Text - -* **MUST** derive messages **only** from: - - * files **staged** for commit (primary), and - * files **awaiting staging** (context). -* **MUST** use the **diffs** to inform content. -* **MUST NOT** invent changes or imply work not present in diffs. - -## 3) Mandatory Preview Flow - -* **ALWAYS** present, before any real commit: - - * file list + brief per-file notes, - * a **draft commit message** (copy-paste ready), - * nothing auto-applied. - ---- - -# Commit Message Format (Normative) - -## A. Subject Line (required) - -``` -<type>(<scope>)<!>: <summary> -``` - -* **type** (lowercase, Conventional Commits): `feat|fix|refactor|perf|docs|test|build|chore|ci|revert` -* **scope**: optional module/package/area (e.g., `api`, `ui/login`, `db`) -* **!**: include when a breaking change is introduced -* **summary**: imperative mood, ≤ 72 chars, no trailing period - -**Examples** - -* `fix(api): handle null token in refresh path` -* `feat(ui/login)!: require OTP after 3 failed attempts` - -## B. Body (optional, when it adds non-obvious value) - -* One blank line after subject. -* Wrap at \~72 chars. -* Explain **what** and **why**, not line-by-line “how”. -* Include brief notes like tests passing or TS/lint issues resolved **only if material**. - -**Body checklist** - -* [ ] Problem/symptom being addressed -* [ ] High-level approach or rationale -* [ ] Risks, tradeoffs, or follow-ups (if any) - -## C. Footer (optional) - -* Issue refs: `Closes #123`, `Refs #456` -* Breaking change (alternative to `!`): - `BREAKING CHANGE: <impact + migration note>` -* Authors: `Co-authored-by: Name <email>` -* Security: `CVE-XXXX-YYYY: <short note>` (if applicable) - ---- - -## Content Guidance - -### Include (when relevant) - -* Specific fixes/features delivered -* Symptoms/problems fixed -* Brief note that tests passed or TS/lint errors resolved - -### Avoid - -* Vague: *improved, enhanced, better* -* Trivialities: tiny docs, one-liners, pure lint cleanups (separate, focused commits if needed) -* Redundancy: generic blurbs repeated across files -* Multi-purpose dumps: keep commits **narrow and focused** -* Long explanations that good inline code comments already cover - -**Guiding Principle:** Let code and inline docs speak. Use commits to highlight what isn’t obvious. - ---- - -# Copy-Paste Templates - -## Minimal (no body) - -```text -<type>(<scope>): <summary> -``` - -## Standard (with body & footer) - -```text -<type>(<scope>)<!>: <summary> - -<why-this-change?> -<what-it-does?> -<risks-or-follow-ups?> - -Closes #<id> -BREAKING CHANGE: <impact + migration> -Co-authored-by: <Name> <email> -``` - ---- - -# Assistant Output Checklist (before showing the draft) - -* [ ] List changed files + 1–2 line notes per file -* [ ] Provide **one** focused draft message (subject/body/footer) -* [ ] Subject ≤ 72 chars, imperative mood, correct `type(scope)!` syntax -* [ ] Body only if it adds non-obvious value -* [ ] No invented changes; aligns strictly with diffs -* [ ] Render as a single copy-paste block for the developer diff --git a/.cursor/rules/workflow/commit_messages.mdc b/.cursor/rules/workflow/commit_messages.mdc new file mode 100644 index 00000000..7ecebfd6 --- /dev/null +++ b/.cursor/rules/workflow/commit_messages.mdc @@ -0,0 +1,196 @@ +# Commit Message Format and Templates + +> **Agent role**: + Reference this file for commit message formatting and templates. + +## Commit Message Format (Normative) + +### A. Subject Line (required) + +``` + +<type>(<scope>)<!>: <summary> + +``` + +- **type** (lowercase, Conventional Commits): + + `feat|fix|refactor|perf|docs|test|build|chore|ci|revert` + +- **scope**: optional module/package/area (e.g., `api`, `ui/login`, `db`) + +- **!**: include when a breaking change is introduced + +- **summary**: imperative mood, ≤ 72 chars, no trailing period + +**Examples** + +- `fix(api): handle null token in refresh path` + +- `feat(ui/login)!: require OTP after 3 failed attempts` + +### B. Body (optional, when it adds non-obvious value) + +- One blank line after subject. + +- Wrap at ~72 chars. + +- Explain **what** and **why**, not line-by-line "how". + +- Include brief notes like tests passing or TS/lint issues resolved + + **only if material**. + +**Body checklist** + +- [ ] Problem/symptom being addressed + +- [ ] High-level approach or rationale + +- [ ] Risks, tradeoffs, or follow-ups (if any) + +### C. Footer (optional) + +- Issue refs: `Closes #123`, `Refs #456` + +- Breaking change (alternative to `!`): + + `BREAKING CHANGE: <impact + migration note>` + +- Authors: `Co-authored-by: Name <email>` + +- Security: `CVE-XXXX-YYYY: <short note>` (if applicable) + +## Content Guidance + +### Include (when relevant) + +- Specific fixes/features delivered + +- Symptoms/problems fixed + +- Brief note that tests passed or TS/lint errors resolved + +### Avoid + +- Vague: *improved, enhanced, better* + +- Trivialities: tiny docs, one-liners, pure lint cleanups (separate, + + focused commits if needed) + +- Redundancy: generic blurbs repeated across files + +- Multi-purpose dumps: keep commits **narrow and focused** + +- Long explanations that good inline code comments already cover + +**Guiding Principle:** Let code and inline docs speak. Use commits to +highlight what isn't obvious. + +## Copy-Paste Templates + +### Minimal (no body) + +```text + +<type>(<scope>): <summary> + +``` + +### Standard (with body & footer) + +```text + +<type>(<scope>)<!>: <summary> + +<why-this-change?> +<what-it-does?> +<risks-or-follow-ups?> + +Closes #<id> +BREAKING CHANGE: <impact + migration> +Co-authored-by: <Name> <email> + +``` + +## Type Descriptions + +### feat + +New feature for the user + +### fix + +Bug fix for the user + +### docs + +Documentation only changes + +### style + +Changes that do not affect the meaning of the code + +### refactor + +Code change that neither fixes a bug nor adds a feature + +### perf + +Code change that improves performance + +### test + +Adding missing tests or correcting existing tests + +### build + +Changes that affect the build system or external dependencies + +### ci + +Changes to CI configuration files and scripts + +### chore + +Other changes that don't modify src or test files + +--- + +**See also**: + +- `.cursor/rules/workflow/version_control.mdc` for + + core version control principles + +- `.cursor/rules/workflow/version_sync.mdc` for version synchronization details + +**Status**: Active commit message guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: version_control.mdc +**Stakeholders**: Development team, AI assistants + +## Model Implementation Checklist + +### Before Creating Commits + +- [ ] **Change Review**: Review all changes to be committed +- [ ] **Scope Assessment**: Determine if changes belong in single or multiple commits +- [ ] **Message Planning**: Plan clear, descriptive commit message +- [ ] **Convention Check**: Review commit message format requirements + +### During Commit Creation + +- [ ] **Type Selection**: Choose appropriate commit type (feat, fix, docs, etc.) +- [ ] **Message Writing**: Write clear, concise commit message +- [ ] **Body Content**: Add detailed description if needed +- [ ] **Breaking Changes**: Document breaking changes with `!` and migration notes + +### After Commit Creation + +- [ ] **Message Review**: Verify commit message follows conventions +- [ ] **Change Validation**: Confirm all intended changes are included +- [ ] **Documentation**: Update any related documentation +- [ ] **Team Communication**: Communicate significant changes to team diff --git a/.cursor/rules/workflow/version_control.mdc b/.cursor/rules/workflow/version_control.mdc new file mode 100644 index 00000000..d501c162 --- /dev/null +++ b/.cursor/rules/workflow/version_control.mdc @@ -0,0 +1,86 @@ +# Directive: Peaceful Co-Existence with Developers + +**Author**: Matthew Raymer +**Date**: 2025-08-19 +**Status**: 🎯 **ACTIVE** - Version control guidelines + +## Core Principles + +### 1) Version-Control Ownership + +- **MUST NOT** run `git add`, `git commit`, or any write action. +- **MUST** leave staging/committing to the developer. + +### 2) Source of Truth for Commit Text + +- **MUST** derive messages **only** from: + + - files **staged** for commit (primary), and + - files **awaiting staging** (context). + +- **MUST** use the **diffs** to inform content. +- **MUST NOT** invent changes or imply work not present in diffs. + +### 3) Mandatory Preview Flow + +- **ALWAYS** present, before any real commit: + + - file list + brief per-file notes, + - a **draft commit message** (copy-paste ready), + - nothing auto-applied. + +### 4) Version Synchronization Requirements + +- **MUST** check for version changes in `package.json` before committing +- **MUST** ensure `CHANGELOG.md` is updated when `package.json` version changes +- **MUST** validate version format consistency between both files +- **MUST** include version bump commits in changelog with + proper semantic versioning + +## Assistant Output Checklist (before showing the draft) + +- [ ] List changed files + 1–2 line notes per file +- [ ] Provide **one** focused draft message (subject/body/footer) +- [ ] Subject ≤ 72 chars, imperative mood, correct `type(scope)!` syntax +- [ ] Body only if it adds non-obvious value +- [ ] No invented changes; aligns strictly with diffs +- [ ] Render as a single copy-paste block for the developer +- [ ] No invented changes; aligns strictly with diffs +- [ ] Render as a single copy-paste block for the developer + +--- + +**See also**: + +- `.cursor/rules/workflow/commit_messages.mdc` for commit message format and + templates +- `.cursor/rules/workflow/version_sync.mdc` for version synchronization details + +**Status**: Active version control guidelines +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: git, package.json, CHANGELOG.md +**Stakeholders**: Development team, AI assistants + +## Model Implementation Checklist + +### Before Version Control Work + +- [ ] **File Analysis**: Review files staged and awaiting staging +- [ ] **Version Check**: Check for version changes in package.json +- [ ] **Changelog Review**: Verify CHANGELOG.md is updated if version changed +- [ ] **Diff Analysis**: Analyze actual changes from git diffs + +### During Version Control Work + +- [ ] **Commit Preview**: Present file list with brief notes per file +- [ ] **Message Draft**: Provide focused draft commit message +- [ ] **Format Validation**: Ensure message follows type(scope)! syntax +- [ ] **Version Sync**: Validate version consistency between files + +### After Version Control Work + +- [ ] **Developer Control**: Leave staging/committing to developer +- [ ] **Message Validation**: Verify message aligns strictly with diffs +- [ ] **Version Validation**: Confirm version format consistency +- [ ] **Documentation**: Update relevant version control documentation diff --git a/.cursor/rules/workflow/version_sync.mdc b/.cursor/rules/workflow/version_sync.mdc new file mode 100644 index 00000000..3f2d4df9 --- /dev/null +++ b/.cursor/rules/workflow/version_sync.mdc @@ -0,0 +1,176 @@ +# Version Synchronization and Changelog Management + +> **Agent role**: Reference this file for version synchronization +> requirements and changelog management. + +## Version Sync Checklist (Before Commit) + +- [ ] `package.json` version matches latest `CHANGELOG.md` entry +- [ ] New version follows semantic versioning + (MAJOR.MINOR.PATCH[-PRERELEASE]) +- [ ] Changelog entry includes all significant changes since last version +- [ ] Version bump commit message follows `build(version): bump to X.Y.Z` + format +- [ ] Breaking changes properly documented with migration notes +- [ ] Alert developer in chat message that version has been updated + +## Version Change Detection + +- **Check for version changes** in staged/unstaged `package.json` +- **Alert developer** if version changed but changelog not updated +- **Suggest changelog update** with proper format and content +- **Validate semantic versioning** compliance + +## Implementation Notes + +### Version Detection + +- Compare `package.json` version field with latest changelog entry +- Use semantic versioning validation +- Check for pre-release version consistency + +### Semantic Validation + +- Ensure version follows `X.Y.Z[-PRERELEASE]` format +- Validate major.minor.patch components +- Handle pre-release suffixes (beta, alpha, rc) + +### Changelog Format + +- Follow [Keep a Changelog](https://keepachangelog.com/) standards +- Use consistent section headers +- Include breaking change notes +- Maintain chronological order + +### Breaking Changes + +- Use `!` in commit message +- Include `BREAKING CHANGE:` in changelog +- Provide migration notes +- Document impact clearly + +### Pre-release Versions + +- Include beta/alpha/rc suffixes consistently +- Update both `package.json` and changelog +- Maintain version number alignment +- Document pre-release status + +## Changelog Sections + +### Added + +- New features +- New capabilities +- New dependencies + +### Changed + +- Changes in existing functionality +- API changes +- Performance improvements + +### Deprecated + +- Soon-to-be removed features +- Migration paths +- Sunset timelines + +### Removed + +- Removed features +- Breaking changes +- Deprecated items + +### Fixed + +- Bug fixes +- Security patches +- Performance fixes + +### Security + +- Security vulnerabilities +- CVE references +- Mitigation steps + +## Version Bump Guidelines + +### Patch (X.Y.Z+1) + +- Bug fixes +- Documentation updates +- Minor improvements + +### Minor (X.Y+1.Z) + +- New features +- Backward-compatible changes +- Significant improvements + +### Major (X+1.Y.Z) + +- Breaking changes +- Major API changes +- Incompatible changes + +## Pre-release Guidelines + +### Beta Versions + +- Feature complete +- Testing phase +- API stable + +### Alpha Versions + +- Early development +- API may change +- Limited testing + +### Release Candidates + +- Final testing +- API frozen +- Production ready + +--- + +**See also**: + +- `.cursor/rules/workflow/version_control.mdc` for core version + control principles +- `.cursor/rules/workflow/commit_messages.mdc` for commit message + format + +**Status**: Active version synchronization guide +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: version_control.mdc +**Stakeholders**: Development team, Release team + +## Model Implementation Checklist + +### Before Version Changes + +- [ ] **Version Review**: Check current version in `package.json` and `CHANGELOG.md` +- [ ] **Change Assessment**: Identify what type of version bump is needed (patch/minor/major) +- [ ] **Breaking Changes**: Review if any changes are breaking and require + major version +- [ ] **Pre-release Status**: Determine if this should be a pre-release version + +### During Version Synchronization + +- [ ] **Semantic Validation**: Ensure version follows `X.Y.Z[-PRERELEASE]` format +- [ ] **Package Update**: Update `package.json` version field +- [ ] **Changelog Entry**: Add entry to `CHANGELOG.md` following Keep a Changelog + format +- [ ] **Breaking Changes**: Document breaking changes with migration notes + if applicable + +### After Version Changes + +- [ ] **Commit Format**: Use `build(version): bump to X.Y.Z` commit message format +- [ ] **Developer Alert**: Alert developer that version has been updated +- [ ] **Validation**: Verify `package.json` and `CHANGELOG.md` are in sync +- [ ] **Pre-release Handling**: Ensure pre-release versions are consistently formatted diff --git a/.dockerignore b/.dockerignore index 777898bc..cfbfe231 100644 --- a/.dockerignore +++ b/.dockerignore @@ -140,7 +140,7 @@ docker-compose* .dockerignore # CI/CD files -.github + .gitlab-ci.yml .travis.yml .circleci diff --git a/.env.test b/.env.test index 5776e66c..dd3d755c 100644 --- a/.env.test +++ b/.env.test @@ -7,7 +7,7 @@ VITE_LOG_LEVEL=info TIME_SAFARI_APP_TITLE="TimeSafari_Test" VITE_APP_SERVER=https://test.timesafari.app # This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not - production). +# This is the claim ID for actions in the BVC project, with the JWT ID on this environment (not production). VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch diff --git a/.eslintrc.js b/.eslintrc.js index fcd19ebe..43e7fbd5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,7 +30,7 @@ module.exports = { }], "no-console": process.env.NODE_ENV === "production" ? "error" : "warn", "no-debugger": process.env.NODE_ENV === "production" ? "error" : "warn", - "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-unnecessary-type-constraint": "off", "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }] diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index 467190be..00000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Playwright Tests -on: - push: - branches: [ main, master ] - pull_request: - branches: [ main, master ] -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - name: Install dependencies - run: npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - name: Run Playwright tests - run: npx playwright test - - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: playwright-report/ - retention-days: 30 diff --git a/.gitignore b/.gitignore index a9f37e49..f628f58b 100644 --- a/.gitignore +++ b/.gitignore @@ -51,11 +51,21 @@ vendor/ # Build logs build_logs/ +# Guard feedback logs (for continuous improvement analysis) +.guard-feedback.log + +# Workflow state file (contains dynamic state, not version controlled) +.cursor/rules/.workflow_state.json + # PWA icon files generated by capacitor-assets icons *.log +# Build outputs +dist/ +build/ + # Generated Android assets and resources (should be generated during build) android/app/src/main/assets/public/ @@ -64,6 +74,14 @@ android/app/src/main/res/drawable*/ android/app/src/main/res/mipmap*/ android/app/src/main/res/values/ic_launcher_background.xml +# Android generated assets (deny-listed in CI) +android/app/src/main/res/mipmap-*/ic_launcher*.png +android/app/src/main/res/drawable*/splash*.png + +# iOS generated assets (deny-listed in CI) +ios/App/App/Assets.xcassets/**/AppIcon*.png +ios/App/App/Assets.xcassets/**/Splash*.png + # Keep these Android configuration files in version control: # - android/app/src/main/assets/capacitor.plugins.json # - android/app/src/main/res/values/strings.xml @@ -128,4 +146,5 @@ electron/out/ # Gradle cache files android/.gradle/file-system.probe android/.gradle/caches/ -coverage \ No newline at end of file +coverage +.husky-enabled \ No newline at end of file diff --git a/.husky/_/husky.sh b/.husky/_/husky.sh new file mode 100755 index 00000000..8de639c0 --- /dev/null +++ b/.husky/_/husky.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env sh +# +# Husky Helper Script +# This file is sourced by all Husky hooks +# +if [ -z "$husky_skip_init" ]; then + debug () { + if [ "$HUSKY_DEBUG" = "1" ]; then + echo "husky (debug) - $1" + fi + } + + readonly hook_name="$(basename -- "$0")" + debug "starting $hook_name..." + + if [ "$HUSKY" = "0" ]; then + debug "HUSKY env variable is set to 0, skipping hook" + exit 0 + fi + + if [ -f ~/.huskyrc ]; then + debug "sourcing ~/.huskyrc" + . ~/.huskyrc + fi + + readonly husky_skip_init=1 + export husky_skip_init + sh -e "$0" "$@" + exitCode="$?" + + if [ $exitCode != 0 ]; then + echo "husky - $hook_name hook exited with code $exitCode (error)" + fi + + if [ $exitCode = 127 ]; then + echo "husky - command not found in PATH=$PATH" + fi + + exit $exitCode +fi diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 00000000..4b8c242d --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +# +# Husky Commit Message Hook +# Validates commit message format using commitlint +# +. "$(dirname -- "$0")/_/husky.sh" + +# Run commitlint but don't fail the commit (|| true) +# This provides helpful feedback without blocking commits +npx commitlint --edit "$1" || true diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..98b8a393 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# +# Husky Pre-commit Hook +# Runs lint-fix and Build Architecture Guard on staged files +# +. "$(dirname -- "$0")/_/husky.sh" + +echo "🔍 Running pre-commit hooks..." + +# Run lint-fix first +echo "📝 Running lint-fix..." +npm run lint-fix || { + echo + echo "❌ Linting failed. Please fix the issues and try again." + echo "💡 To bypass this check for emergency commits, use:" + echo " git commit --no-verify" + echo + exit 1 +} + +# Then run Build Architecture Guard +echo "🏗️ Running Build Architecture Guard..." +bash ./scripts/build-arch-guard.sh --staged || { + echo + echo "❌ Build Architecture Guard failed. Please fix the issues and try again." + echo "💡 To bypass this check for emergency commits, use:" + echo " git commit --no-verify" + echo + exit 1 +} + +echo "✅ All pre-commit checks passed!" + diff --git a/.husky/pre-push b/.husky/pre-push new file mode 100755 index 00000000..12a16ea5 --- /dev/null +++ b/.husky/pre-push @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# +# Husky Pre-push Hook +# Runs Build Architecture Guard to check commits being pushed +# +. "$(dirname -- "$0")/_/husky.sh" + +echo "🔍 Running Build Architecture Guard (pre-push)..." + +# Get the remote branch we're pushing to +REMOTE_BRANCH="origin/$(git rev-parse --abbrev-ref HEAD)" + +# Check if remote branch exists +if git show-ref --verify --quiet "refs/remotes/$REMOTE_BRANCH"; then + RANGE="$REMOTE_BRANCH...HEAD" +else + # If remote branch doesn't exist, check last commit + RANGE="HEAD~1..HEAD" +fi + +bash ./scripts/build-arch-guard.sh --range "$RANGE" || { + echo + echo "💡 To bypass this check for emergency pushes, use:" + echo " git push --no-verify" + echo + exit 1 +} diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc new file mode 100644 index 00000000..6e881a27 --- /dev/null +++ b/.markdownlint-cli2.jsonc @@ -0,0 +1,53 @@ +{ + // Markdownlint configuration for TimeSafari .cursor/rules + "config": { + // Core formatting rules that can be auto-fixed + "MD013": { + "line_length": 80, + "code_blocks": false, + "tables": false, + "headings": false + }, + "MD012": true, // No multiple consecutive blank lines + "MD022": true, // Headings should be surrounded by blank lines + "MD031": true, // Fenced code blocks should be surrounded by blank lines + "MD032": true, // Lists should be surrounded by blank lines + "MD047": true, // Files should end with a single newline + "MD009": true, // No trailing spaces + "MD010": true, // No hard tabs + "MD004": { "style": "dash" }, // Consistent list markers + "MD029": { "style": "ordered" }, // Ordered list item prefix + + // Disable rules that conflict with existing content structure + "MD041": false, // First line heading requirement + "MD025": false, // Multiple top-level headings + "MD024": false, // Duplicate headings + "MD036": false, // Emphasis as headings + "MD003": false, // Heading style consistency + "MD040": false, // Fenced code language + "MD055": false, // Table pipe style + "MD056": false, // Table column count + "MD034": false, // Bare URLs + "MD023": false // Heading indentation + }, + + "globs": [ + ".cursor/rules/**/*.mdc", + "*.md", + "*.markdown", + "scripts/**/*.md", + "src/**/*.md", + "test-playwright/**/*.md", + "resources/**/*.md", + "doc/**/*.md", + "ios/**/*.md", + "electron/**/*.md" + ], + "ignores": [ + "node_modules/**", + ".git/**", + "**/node_modules/**", + "**/dist/**", + "**/build/**" + ] +} \ No newline at end of file diff --git a/.markdownlint.json b/.markdownlint.json index 2f71b81b..2f13abeb 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -1 +1,27 @@ -{"MD013": {"code_blocks": false}} +{ + "MD013": { + "line_length": 80, + "code_blocks": false, + "tables": false, + "headings": false + }, + "MD012": true, + "MD022": true, + "MD031": true, + "MD032": true, + "MD047": true, + "MD009": true, + "MD010": true, + "MD004": { "style": "dash" }, + "MD029": { "style": "ordered" }, + "MD041": false, + "MD025": false, + "MD024": false, + "MD036": false, + "MD003": false, + "MD040": false, + "MD055": false, + "MD056": false, + "MD034": false, + "MD023": false +} \ No newline at end of file diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..a9d08739 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +18.19.0 diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..a9d08739 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.19.0 diff --git a/BUILDING.md b/BUILDING.md index e1e94fcd..1ac4ae9d 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -13,7 +13,7 @@ npm run build:web:serve -- --test # Start with test endorser server npm run build:web:dev # Start development server with hot reload with local endorser server npm run build:web:prod # Production build -# 📱 Mobile Development +# 📱 Mobile Development npm run build:ios # iOS build (opens Xcode) npm run build:android # Android build (opens Android Studio) @@ -31,6 +31,7 @@ npm run clean:all # Clean all platforms ### Development Workflow #### 1. First-Time Setup + ```bash # Install dependencies npm install @@ -40,6 +41,7 @@ npm run test:web # Run web tests to verify setup ``` #### 2. Daily Development + ```bash # Start web development server npm run build:web:dev # Opens http://localhost:8080 @@ -48,11 +50,12 @@ npm run build:web:dev # Opens http://localhost:8080 npm run build:ios # Opens Xcode with iOS project npm run build:android # Opens Android Studio with Android project -# For desktop development +# For desktop development npm run build:electron:dev # Runs Electron app directly ``` #### 3. Testing Your Changes + ```bash # Test web functionality npm run test:web # Run web tests @@ -66,6 +69,7 @@ npm run build:android:test:run # Build and run on Android emulator ``` #### 4. Production Builds + ```bash # Build for production npm run build:web:prod # Web production build @@ -74,9 +78,83 @@ npm run build:android:prod # Android production build npm run build:electron:prod # Electron production build ``` +### Build Architecture Guard + +The Build Architecture Guard protects your build system by enforcing documentation updates when build-critical files are modified. This ensures that all build changes are properly documented in `BUILDING.md`. + +#### How It Works + +- **Pre-commit Hook**: Automatically checks staged files before each commit +- **Protected Files**: Build scripts, config files, and platform-specific code +- **Documentation Requirement**: `BUILDING.md` must be updated alongside build changes +- **Automatic Enforcement**: Git hooks prevent commits without proper documentation +- **Feedback Collection**: Continuously improves through usage pattern analysis + +#### Protected File Patterns + +The guard monitors these sensitive paths: +- `vite.config.*` - Build configuration +- `scripts/**` - Build and utility scripts +- `electron/**` - Desktop application code +- `android/**` - Android platform code +- `ios/**` - iOS platform code +- `capacitor.config.ts` - Mobile configuration +- `capacitor-assets.config.json` - Android asset configuration +- `resources/**` - Source assets for Android resource generation +- `package.json` - Dependencies and scripts + +#### Enhanced Android Protection + +The guard now provides enhanced protection for Android build system changes: + +- **Asset Validation**: Protects `validate_android_assets()` function and resource paths +- **Resource Generation**: Monitors `capacitor-assets` integration and verification +- **API Routing**: Protects platform-specific IP handling (emulator vs physical device) +- **Build Modes**: Validates development/test/production mode handling +- **Resource Fallback**: Protects automatic regeneration of missing Android resources + +#### Using the Guard + +```bash +# Test the guard locally +./scripts/build-arch-guard.sh --staged + +# Analyze guard effectiveness (for maintainers) +./scripts/build-arch-guard.sh --feedback + +# Bypass for emergency commits (use sparingly) +git commit --no-verify + +# Setup the guard +npm run guard:setup +``` + +#### Troubleshooting + +If you encounter `mapfile: command not found` errors: +```bash +# Ensure script is executable +chmod +x scripts/build-arch-guard.sh + +# Test the script +./scripts/build-arch-guard.sh --help +``` + +#### Feedback and Continuous Improvement + +The guard system includes feedback mechanisms for continuous improvement: + +- **Automatic Logging**: All guard executions are logged for analysis +- **Pattern Analysis**: Identifies false positives/negatives and missing patterns +- **Maintainer Insights**: Use `--feedback` command to analyze guard effectiveness +- **Continuous Updates**: Guard rules and patterns are updated based on feedback + +**Note**: The guard is active and will block commits that modify build files without updating `BUILDING.md`. Recent enhancements provide better Android build system protection and feedback collection for continuous improvement. + ### Environment Configuration #### Quick Environment Setup + ```bash # Copy environment template (if available) cp .env.example .env.development @@ -89,6 +167,7 @@ cp .env.example .env.development ``` #### Platform-Specific Development + - **Web**: Uses `localhost:3000` for APIs by default - **iOS Simulator**: Uses `localhost:3000` for APIs - **Android Emulator**: Uses `10.0.2.2:3000` for APIs @@ -97,6 +176,7 @@ cp .env.example .env.development ### Troubleshooting Quick Fixes #### Common Issues + ```bash # Clean and rebuild npm run clean:all @@ -109,13 +189,18 @@ npm run clean:android npm run build:ios # Regenerates iOS project npm run build:android # Regenerates Android project +# Fix Android asset issues +npm run assets:validate:android # Validates and regenerates missing Android assets + # Check environment npm run test:web # Verifies web setup ``` #### Platform-Specific Issues + - **iOS**: Ensure Xcode and Command Line Tools are installed - **Android**: Ensure Android Studio and SDK are configured + - If you encounter "resource drawable/splash not found" errors, run `npm run assets:validate:android` - **Electron**: Ensure platform-specific build tools are installed ### Next Steps @@ -166,7 +251,7 @@ npm run build:web:dev # Start development server with hot reload npm run build:web # Development build (starts dev server with hot reload) npm run build:web:test # Test environment build (optimized for testing) npm run build:web:prod # Production build (optimized for production) -npm run build:web:serve # Build and serve locally (builds then serves) +npm run build:web:serve # Build and serve locally for production testing # Docker builds npm run build:web:docker # Development build with Docker containerization @@ -184,6 +269,12 @@ Start the development server using `npm run build:web:dev` or `npm run build:web 2. The built files will be in the `dist` directory 3. To test the production build locally, use `npm run build:web:serve` (builds then serves) +**Why Use `serve`?** +- **Production Testing**: Test your optimized production build locally before deployment +- **SPA Routing Validation**: Verify deep linking and navigation work correctly (handles routes like `/discover`, `/account`) +- **Performance Testing**: Test the minified and optimized build locally +- **Deployment Validation**: Ensure built files work correctly when served by a real HTTP server + You'll likely want to use test locations for the Endorser & image & partner servers; see "DEFAULT_ENDORSER_API_SERVER" & "DEFAULT_IMAGE_API_SERVER" & "DEFAULT_PARTNER_API_SERVER" below. ### Web Build Script Details @@ -203,7 +294,7 @@ All web build commands use the `./scripts/build-web.sh` script, which provides: - **Clean Build**: Removes previous `dist/` directory - **Vite Build**: Executes `npx vite build --config vite.config.web.mts` - **Docker Support**: Optional Docker containerization -- **Local Serving**: Built-in HTTP server for testing builds +- **Local Serving**: Built-in HTTP server for testing builds with SPA routing support **Direct Script Usage:** @@ -222,6 +313,7 @@ All web build commands use the `./scripts/build-web.sh` script, which provides: ``` **Script Flow:** + 1. **Environment Validation**: Check prerequisites (Node.js, npm, etc.) 2. **Environment Setup**: Load `.env` files, set NODE_ENV 3. **Clean Dist**: Remove previous build artifacts @@ -230,38 +322,57 @@ All web build commands use the `./scripts/build-web.sh` script, which provides: 6. **Optional Serve**: Start local HTTP server if requested **Exit Codes:** + - `1` - Web cleanup failed -- `2` - Environment setup failed +- `2` - Environment setup failed - `3` - Vite build failed - `4` - Docker build failed - `5` - Serve command failed - `6` - Invalid build mode +### Local Serving with `serve` + +The `serve` functionality provides a local HTTP server for testing production builds: + +**What It Does:** +1. **Builds** the application using Vite +2. **Serves** the built files from the `dist/` directory +3. **Handles SPA Routing** - serves `index.html` for all routes (fixes 404s on `/discover`, `/account`, etc.) + +**Server Options:** +- **Primary**: `npx serve -s dist -l 8080` (recommended - full SPA support) +- **Fallback**: Python HTTP server (limited SPA routing support) + +**Use Cases:** +- Testing production builds before deployment +- Validating SPA routing behavior +- Performance testing of optimized builds +- Debugging production build issues locally + ### Compile and minify for test & production -* If there are DB changes: before updating the test server, open browser(s) with current version to test DB migrations. +- If there are DB changes: before updating the test server, open browser(s) with +current version to test DB migrations. -* Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run `npm install`. +- Update the ClickUp tasks & CHANGELOG.md & the version in package.json, run +`npm install`. -* Run a build to make sure package-lock version is updated, linting works, etc: `npm install && npm run build:web` +- Run a build to make sure package-lock version is updated, linting works, etc: +`npm install && npm run build:web` -* Commit everything (since the commit hash is used the app). +- Commit everything (since the commit hash is used the app). -* Put the commit hash in the changelog (which will help you remember to bump the version in the step later). +- Put the commit hash in the changelog (which will help you remember to bump the + version in the step later). -* Tag with the new version, [online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or `git tag 1.0.2 && git push origin 1.0.2`. +- Tag with the new version, +[online](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa/releases) or +`git tag 1.0.2 && git push origin 1.0.2`. -* For test, build the app (because test server is not yet set up to build): +- For test, build the app: ```bash -TIME_SAFARI_APP_TITLE="TimeSafari_Test" \ -VITE_APP_SERVER=https://test.timesafari.app \ -VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZYYPS272F \ -VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch \ -VITE_DEFAULT_IMAGE_API_SERVER=https://test-image-api.timesafari.app \ -VITE_DEFAULT_PARTNER_API_SERVER=https://test-partner-api.endorser.ch \ -VITE_DEFAULT_PUSH_SERVER=https://test.timesafari.app \ -VITE_PASSKEYS_ENABLED=true npm run build:web +npm run build:web:test ``` ... and transfer to the test server: @@ -274,25 +385,28 @@ rsync -azvu -e "ssh -i ~/.ssh/..." dist ubuntutest@test.timesafari.app:time-safa (Note: The test BVC_MEETUPS_PROJECT_CLAIM_ID does not resolve as a URL because it's only in the test DB and the prod redirect won't redirect there.) -* For prod, get on the server and run the correct build: +- For prod, get on the server and run the correct build: ... and log onto the server: -* `pkgx +npm sh` +- `pkgx +npm sh` -* `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout 1.0.2 && npm install && npm run build:web:prod && cd -` +- `cd crowd-funder-for-time-pwa && git checkout master && git pull && git checkout +1.0.2 && npm install && npm run build:web:prod && cd -` (The plain `npm run build:web:prod` uses the .env.production file.) -* Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev-2 && mv crowd-funder-for-time-pwa/dist time-safari/` +- Back up the time-safari/dist folder & deploy: `mv time-safari/dist time-safari-dist-prev-2 && mv crowd-funder-for-time-pwa/dist time-safari/` -* Record the new hash in the changelog. Edit package.json to increment version & add "-beta", `npm install`, commit, and push. Also record what version is on production. +- Record the new hash in the changelog. Edit package.json to increment version & +add "-beta", `npm install`, commit, and push. Also record what version is on production. ## Docker Deployment -The application can be containerized using Docker for consistent deployment across environments. +The application can be containerized using Docker for consistent deployment across +environments. -### Prerequisites +### Docker Prerequisites - Docker installed on your system - Docker Compose (optional, for multi-container setups) @@ -402,7 +516,10 @@ docker run -d \ ## Desktop Build (Electron) -TimeSafari's Electron build system provides comprehensive desktop application packaging and distribution capabilities across Windows, macOS, and Linux platforms. The system supports multiple build modes, environment configurations, and package formats. +TimeSafari's Electron build system provides comprehensive desktop application +packaging and distribution capabilities across Windows, macOS, and Linux +platforms. The system supports multiple build modes, environment configurations, +and package formats. ### Electron Build Commands @@ -436,15 +553,18 @@ npm run clean:electron # Clean Electron build artifacts #### Development Build -Start development build using `npm run build:electron:dev` (builds and runs the app directly). +Start development build using `npm run build:electron:dev` (builds and runs the +app directly). #### Production Build -Run production builds using the commands above. Production builds create platform-specific packages. +Run production builds using the commands above. Production builds create +platform-specific packages. #### Package-Specific Builds -Create platform-specific packages using the commands above. These build the app and create installable packages. +Create platform-specific packages using the commands above. These build the app +and create installable packages. ### Single Instance Enforcement @@ -484,29 +604,30 @@ The Electron app enforces single-instance operation to prevent: The Electron build process follows a multi-stage approach: -``` +```text 1. Web Build (Vite) → 2. Capacitor Sync → 3. TypeScript Compile → 4. Package ``` -**Stage 1: Web Build** +#### **Stage 1: Web Build** - Vite builds web assets with Electron-specific configuration - Environment variables loaded based on build mode - Assets optimized for desktop application -**Stage 2: Capacitor Sync** +#### **Stage 2: Capacitor Sync** - Copies web assets to Electron app directory -- Syncs Capacitor configuration and plugins +- Uses Electron-specific Capacitor configuration (not copied from main config) +- Syncs Capacitor plugins for Electron platform - Prepares native module bindings -**Stage 3: TypeScript Compile** +#### **Stage 3: TypeScript Compile** - Compiles Electron main process TypeScript - Rebuilds native modules for target platform - Generates production-ready JavaScript -**Stage 4: Package Creation** +#### **Stage 4: Package Creation** - Creates platform-specific installers - Generates distribution packages @@ -566,6 +687,7 @@ npm run build:electron:windows:prod ``` **Configuration**: + - NSIS installer with custom options - Desktop and Start Menu shortcuts - Elevation permissions for installation @@ -588,6 +710,7 @@ npm run build:electron:mac:prod ``` **Configuration**: + - Universal binary (x64 + arm64) - DMG installer with custom branding - App Store compliance (when configured) @@ -610,12 +733,13 @@ npm run build:electron:linux:prod ``` **Configuration**: + - AppImage for universal distribution - DEB package for Debian-based systems - RPM package for Red Hat-based systems - Desktop integration -### Package-Specific Builds +### Desktop Package-Specific Builds #### AppImage Package @@ -634,6 +758,7 @@ npm run build:electron:appimage:prod ``` **Features**: + - Single file distribution - No installation required - Portable across Linux distributions @@ -656,6 +781,7 @@ npm run build:electron:deb:prod ``` **Features**: + - Native package management - Dependency resolution - System integration @@ -678,6 +804,7 @@ npm run build:electron:dmg:prod ``` **Features**: + - Native macOS installer - Custom branding and layout - Drag-and-drop installation @@ -742,7 +869,7 @@ npm run build:electron:dmg:test # DMG test build npm run build:electron:dmg:prod # DMG production build ``` -#### Direct Script Usage +#### Direct Script Usage Reference All npm scripts use the underlying `./scripts/build-electron.sh` script: @@ -766,9 +893,9 @@ npm run clean:electron # Clean Electron build artifacts ### Build Output Structure -#### Development Build +#### Development Build Structure -``` +```text electron/ ├── app/ # Web assets ├── build/ # Compiled TypeScript @@ -776,9 +903,9 @@ electron/ └── node_modules/ # Dependencies ``` -#### Production Build +#### Production Build Output -``` +```text electron/ ├── app/ # Web assets ├── build/ # Compiled TypeScript @@ -796,7 +923,8 @@ electron/ For public distribution on macOS, you need to code sign and notarize your app: -1. Set up environment variables: +##### 1. Set up environment variables + ```bash export CSC_LINK=/path/to/your/certificate.p12 export CSC_KEY_PASSWORD=your_certificate_password @@ -804,7 +932,8 @@ export APPLE_ID=your_apple_id export APPLE_ID_PASSWORD=your_app_specific_password ``` -2. Build with signing: +##### 2. Build with signing + ```bash npm run build:electron:mac:prod ``` @@ -813,13 +942,15 @@ npm run build:electron:mac:prod For Windows distribution, configure Authenticode signing: -1. Set up environment variables: +##### 1. Set up desktop environment variables + ```bash export CSC_LINK=/path/to/your/certificate.p12 export CSC_KEY_PASSWORD=your_certificate_password ``` -2. Build with signing: +##### 2. Build desktop with signing + ```bash npm run build:electron:windows:prod ``` @@ -829,12 +960,14 @@ npm run build:electron:windows:prod #### Linux - **AppImage**: Make executable and run + ```bash chmod +x electron/dist/TimeSafari-*.AppImage ./electron/dist/TimeSafari-*.AppImage ``` - **DEB**: Install and run + ```bash sudo dpkg -i electron/dist/timesafari_*_amd64.deb timesafari @@ -849,6 +982,7 @@ timesafari 3. Launch from Applications Note: If you get a security warning when running the app: + 1. Right-click the app 2. Select "Open" 3. Click "Open" in the security dialog @@ -863,6 +997,7 @@ Note: If you get a security warning when running the app: #### Common Build Issues **TypeScript Compilation Errors**: + ```bash # Clean and rebuild npm run clean:electron @@ -870,30 +1005,35 @@ npm run build:electron:dev ``` **Native Module Issues**: + ```bash # Rebuild native modules npm run build:electron:dev ``` **Asset Copy Issues**: + ```bash # Verify Capacitor sync npx cap sync electron ``` -#### Platform-Specific Issues +#### Platform-Specific Building Issues **Windows**: + - Ensure Windows Build Tools installed - Check NSIS installation - Verify code signing certificates **macOS**: + - Install Xcode Command Line Tools - Configure code signing certificates - Check app notarization requirements **Linux**: + - Install required packages (rpm-tools, etc.) - Check AppImage dependencies - Verify desktop integration @@ -984,6 +1124,7 @@ The recommended way to build for iOS is using the automated build script: ``` The script handles all the necessary steps including: + - Environment setup and validation - Web asset building (Capacitor mode) - Capacitor synchronization @@ -1004,11 +1145,11 @@ If you need to build manually or want to understand the individual steps: #### Each Release -0. First time (or if dependencies change): +##### 0. First time (or if dependencies change) - - `pkgx +rubygems.org sh` +- `pkgx +rubygems.org sh` - - ... and you may have to fix these, especially with pkgx: +- ... and you may have to fix these, especially with pkgx: ```bash gem_path=$(which gem) @@ -1017,60 +1158,46 @@ If you need to build manually or want to understand the individual steps: export GEM_PATH=$shortened_path ``` -1. Build the web assets & update ios: +##### 1. Bump the version in package.json, then here ```bash - rm -rf dist - npm run build:web - npm run build:capacitor - npx cap sync ios - ``` - - - If that fails with "Could not find..." then look at the "gem_path" instructions above. - -3. Copy the assets: - - ```bash - # It makes no sense why capacitor-assets will not run without these but it actually changes the contents. - mkdir -p ios/App/App/Assets.xcassets/AppIcon.appiconset - echo '{"images":[]}' > ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json - mkdir -p ios/App/App/Assets.xcassets/Splash.imageset - echo '{"images":[]}' > ios/App/App/Assets.xcassets/Splash.imageset/Contents.json - npx capacitor-assets generate --ios - ``` - -4. Bump the version to match Android & package.json: - - ``` - cd ios/App && xcrun agvtool new-version 39 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.6;/g" App.xcodeproj/project.pbxproj && cd - + cd ios/App && xcrun agvtool new-version 40 && perl -p -i -e "s/MARKETING_VERSION = .*;/MARKETING_VERSION = 1.0.7;/g" App.xcodeproj/project.pbxproj && cd - # Unfortunately this edits Info.plist directly. #xcrun agvtool new-marketing-version 0.4.5 ``` -5. Open the project in Xcode: +##### 2. Build - ```bash - npx cap open ios - ``` + Here's prod. Also available: test, dev + + ```bash + npm run build:ios:prod + ``` -6. Use Xcode to build and run on simulator or device. +3.1. Use Xcode to build and run on simulator or device. - * Select Product -> Destination with some Simulator version. Then click the run arrow. +- Select Product -> Destination with some Simulator version. Then click the run arrow. -7. Release +3.2. Use Xcode to release. - * Someday: Under "General" we want to rename a bunch of things to "Time Safari" - * Choose Product -> Destination -> Any iOS Device - * Choose Product -> Archive - * This will trigger a build and take time, needing user's "login" keychain password (user's login password), repeatedly. - * If it fails with `building for 'iOS', but linking in dylib (.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run XCode outside that terminal (ie. not with `npx cap open ios`). - * Click Distribute -> App Store Connect - * In AppStoreConnect, add the build to the distribution. You may have to remove the current build with the "-" when you hover over it, then "Add Build" with the new build. - * May have to go to App Review, click Submission, then hover over the build and click "-". - * It can take 15 minutes for the build to show up in the list of builds. - * You'll probably have to "Manage" something about encryption, disallowed in France. - * Then "Save" and "Add to Review" and "Resubmit to App Review". - * Eventually it'll be "Ready for Distribution" which means +- Someday: Under "General" we want to rename a bunch of things to "Time Safari" +- Choose Product -> Destination -> Any iOS Device +- Choose Product -> Archive + - This will trigger a build and take time, needing user's "login" keychain + password (user's login password), repeatedly. + - If it fails with `building for 'iOS', but linking in dylib + (.../.pkgx/zlib.net/v1.3.0/lib/libz.1.3.dylib) built for 'macOS'` then run + XCode outside that terminal (ie. not with `npx cap open ios`). + - Click Distribute -> App Store Connect +- In AppStoreConnect, add the build to the distribution. You may have to remove + the current build with the "-" when you hover over it, then "Add Build" with the + new build. + - May have to go to App Review, click Submission, then hover over the build + and click "-". + - It can take 15 minutes for the build to show up in the list of builds. + - You'll probably have to "Manage" something about encryption, disallowed in France. + - Then "Save" and "Add to Review" and "Resubmit to App Review". +- Eventually it'll be "Ready for Distribution" which means ### Android Build @@ -1083,7 +1210,7 @@ Prerequisites: Android Studio with Java SDK installed npm run build:android # Development build (builds and opens Android Studio) npm run build:android:dev # Development build (builds and opens Android Studio) npm run build:android:test # Test build (builds for testing environment) -npm run build:android:prod # Production build (builds for production environment) +npm run build:android:prod # Production build (builds for production environment). # Auto-run builds npm run build:android:test:run # Test build with auto-run (builds then runs on emulator) @@ -1105,7 +1232,70 @@ npm run build:android:assets # Generate assets only npm run build:android:deploy # Build and deploy to connected device ``` -#### Automated Build Script +#### Android Asset Validation + +The Android build system now includes automatic asset validation to prevent build failures caused by missing resources. This system: + +- **Validates Source Assets**: Checks that required source files exist in `resources/` +- **Checks Android Resources**: Verifies that generated Android resources are present +- **Auto-Regenerates**: Automatically regenerates missing resources when detected +- **Provides Clear Errors**: Gives helpful guidance when issues occur + +##### Asset Validation Commands + +```bash +# Validate and regenerate Android assets if needed +npm run assets:validate:android + +# Alternative command for asset validation +./scripts/build-android.sh --assets-only + +# Check asset configuration only (no regeneration) +npm run assets:validate +``` + +##### What Gets Validated + +**Source Assets (Required):** +- `resources/icon.png` - App icon source +- `resources/splash.png` - Splash screen source +- `resources/splash_dark.png` - Dark mode splash source + +**Android Resources (Generated):** +- `android/app/src/main/res/drawable/splash.png` - Splash screen drawable +- `android/app/src/main/res/mipmap-*/ic_launcher.png` - App icons for all densities +- `android/app/src/main/res/mipmap-*/ic_launcher_round.png` - Round app icons for all densities + +##### Automatic Validation + +Asset validation runs automatically during all Android builds: + +```bash +# All these commands now include asset validation +npm run build:android:studio +npm run build:android:prod +npm run build:android:debug +``` + +If validation fails, the build stops with clear error messages and guidance on how to fix the issues. + +##### Troubleshooting Asset Issues + +If you encounter asset-related build failures: + +```bash +# Check what's missing +npm run assets:validate:android + +# Clean and regenerate everything +npm run clean:android +npm run assets:validate:android +npm run build:android:studio +``` + +For more detailed information, see [Android Asset Validation Documentation](doc/android-asset-validation.md). + +#### Android Automated Build Script The recommended way to build for Android is using the automated build script: @@ -1123,37 +1313,30 @@ The recommended way to build for Android is using the automated build script: ./scripts/build-android.sh --help ``` -#### Manual Build Process +#### Android Manual Build Process -1. Build the web assets: +##### 1. Bump the version in package.json, then here: android/app/build.gradle - ```bash - rm -rf dist - npm run build:web - npm run build:capacitor - ``` + ```bash + perl -p -i -e 's/versionCode .*/versionCode 40/g' android/app/build.gradle + perl -p -i -e 's/versionName .*/versionName "1.0.7"/g' android/app/build.gradle + ``` -2. Update Android project with latest build: +##### 2. Build - ```bash - npx cap sync android - ``` + Here's prod. Also available: test, dev -3. Copy the assets + ```bash + npm run build:android:prod + ``` - ```bash - npx capacitor-assets generate --android - ``` - -4. Bump version to match iOS & package.json: android/app/build.gradle - -5. Open the project in Android Studio: +##### 3. Open the project in Android Studio ```bash npx cap open android ``` -6. Use Android Studio to build and run on emulator or device. +##### 4. Use Android Studio to build and run on emulator or device ## Android Build from the console @@ -1172,9 +1355,10 @@ cd - ... or, to create a signed release: -* Setup by adding the app/gradle.properties.secrets file (see properties at top of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file -* In app/build.gradle, bump the versionCode and maybe the versionName -* Then `bundleRelease`: +- Setup by adding the app/gradle.properties.secrets file (see properties at top + of app/build.gradle) and the app/time-safari-upload-key-pkcs12.jks file +- In app/build.gradle, bump the versionCode and maybe the versionName +- Then `bundleRelease`: ```bash cd android @@ -1186,13 +1370,14 @@ cd - At play.google.com/console: -- Go to the Testing Track (eg. Closed). +- Go to Production or the Closed Testing and either Create Track or Manage Track. - Click "Create new release". -- Upload the `aab` file. +- Upload the `aab` file from: app/build/outputs/bundle/release/app-release.aab - Hit "Next". - Save, go to "Publishing Overview" as prompted, and click "Send changes for review". -- Note that if you add testers, you have to go to "Publishing Overview" and send those changes or your (closed) testers won't see it. +- Note that if you add testers, you have to go to "Publishing Overview" and send + those changes or your (closed) testers won't see it. ### Capacitor Operations @@ -1290,7 +1475,7 @@ npm run lint-fix # Fix linting issues Use the commands above to check and fix code quality issues. -## Build Architecture +## Code Build Architecture ### Web Build Process @@ -1315,19 +1500,22 @@ Use the commands above to check and fix code quality issues. 4. **Native Build**: Platform-specific compilation 5. **Package Creation**: APK/IPA generation -## Environment Configuration +## Architecture Environment Configuration ### Environment Files The build system supports multiple environment file patterns for different scenarios: #### Primary Environment Files + - `.env.development` - Development environment (local development) - `.env.test` - Testing environment (staging/testing) - `.env.production` - Production environment (production deployment) #### Fallback and Local Files -- `.env` - General fallback environment file (loaded if mode-specific file doesn't exist) + +- `.env` - General fallback environment file (loaded if mode-specific file + doesn't exist) - `.env.local` - Local development overrides (gitignored) - `.env.*.local` - Mode-specific local overrides (gitignored) @@ -1351,6 +1539,7 @@ The build system supports multiple environment file patterns for different scena ### Key Environment Variables #### API Server Configuration + ```bash # API Servers (Environment-specific) VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch @@ -1369,6 +1558,7 @@ VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZY #### Environment-Specific Configurations **Development Environment** (`.env.development`): + ```bash # Development API Servers (Local) VITE_DEFAULT_ENDORSER_API_SERVER=http://localhost:3000 @@ -1378,6 +1568,7 @@ VITE_APP_SERVER=http://localhost:8080 ``` **Test Environment** (`.env.test`): + ```bash # Test API Servers VITE_DEFAULT_ENDORSER_API_SERVER=https://test-api.endorser.ch @@ -1387,6 +1578,7 @@ VITE_APP_SERVER=https://test.timesafari.app ``` **Production Environment** (`.env.production`): + ```bash # Production API Servers VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch @@ -1398,16 +1590,19 @@ VITE_APP_SERVER=https://timesafari.app ### Platform-Specific Overrides #### Android Development + - **Emulator**: Uses `http://10.0.2.2:3000` (Android emulator default) - **Physical Device**: Uses custom IP address (e.g., `http://192.168.1.100:3000`) #### iOS Development + - **Simulator**: Uses `http://localhost:3000` (iOS simulator default) - **Physical Device**: Uses custom IP address (e.g., `http://192.168.1.100:3000`) ### Environment Loading Process 1. **Build Script Initialization** + ```bash # scripts/common.sh - setup_build_env() if [ "$BUILD_MODE" = "development" ]; then @@ -1417,6 +1612,7 @@ VITE_APP_SERVER=https://timesafari.app ``` 2. **Platform-Specific Overrides** + ```bash # scripts/build-android.sh if [ "$BUILD_MODE" = "development" ]; then @@ -1426,13 +1622,14 @@ VITE_APP_SERVER=https://timesafari.app ``` 3. **Environment File Loading** + ```bash # scripts/build-web.sh local env_file=".env.$BUILD_MODE" # .env.development, .env.test, .env.production if [ -f "$env_file" ]; then load_env_file "$env_file" fi - + # Fallback to .env if [ -f ".env" ]; then load_env_file ".env" @@ -1440,6 +1637,7 @@ VITE_APP_SERVER=https://timesafari.app ``` 4. **Application Usage** + ```typescript // src/constants/app.ts export const DEFAULT_ENDORSER_API_SERVER = @@ -1447,9 +1645,9 @@ VITE_APP_SERVER=https://timesafari.app AppString.PROD_ENDORSER_API_SERVER; ``` -## Troubleshooting +## Building Troubleshooting -### Common Issues +### Common Issues Building #### Build Failures @@ -1474,29 +1672,34 @@ npm run build:ios:assets npm run build:android:assets ``` -### Platform-Specific Issues +### More Platform-Specific Building Issues + +#### Building on Windows -#### Windows - Ensure Windows Build Tools installed - Check NSIS installation - Verify code signing certificates -#### macOS +#### Building on macOS + - Install Xcode Command Line Tools - Configure code signing certificates - Check app notarization requirements -#### Linux +#### Building on Linux + - Install required packages (rpm-tools, etc.) - Check AppImage dependencies - Verify desktop integration #### iOS + - Install Xcode and Command Line Tools - Configure signing certificates - Check provisioning profiles #### Android + - Install Android Studio and SDK - Configure signing keys - Check device/emulator setup @@ -1515,37 +1718,45 @@ npm run build:android:assets ### Build Process Overview -TimeSafari's build system follows a multi-stage process that prepares assets, combines scripts, and generates platform-specific outputs. +TimeSafari's build system follows a multi-stage process that prepares assets, +combines scripts, and generates platform-specific outputs. #### Pre-Build Preparation -**1. Service Worker Script Preparation** +##### 1. Service Worker Script Preparation + ```bash # Optional: Format third-party service worker scripts npx prettier --write ./sw_scripts/ ``` **What this does:** -- Formats cryptographic libraries (`nacl.js`, `noble-curves.js`, `noble-hashes.js`, etc.) + +- Formats cryptographic libraries (`nacl.js`, `noble-curves.js`, + `noble-hashes.js`, etc.) - These scripts are automatically combined during the build process - Improves readability and makes version control diffs cleaner - **Note**: This is optional and only needed when updating third-party scripts **2. Automatic Pre-Build Steps** The `prebuild` script automatically runs before any build: + ```json "prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js" ``` **What happens automatically:** + - **ESLint**: Checks and fixes code formatting in `src/` -- **Script Combination**: `sw_combine.js` combines all `sw_scripts/*.js` files into `sw_scripts-combined.js` +- **Script Combination**: `sw_combine.js` combines all `sw_scripts/*.js` files + into `sw_scripts-combined.js` - **WASM Copy**: `copy-wasm.js` copies SQLite WASM files to `public/wasm/` -#### Build Architecture +#### Build Process Architecture **Web Build Process:** -``` + +```text 1. Pre-Build: ESLint + Script Combination + WASM Copy 2. Environment Setup: Load .env files, set NODE_ENV 3. Vite Build: Bundle web assets with PWA support @@ -1554,7 +1765,8 @@ The `prebuild` script automatically runs before any build: ``` **Electron Build Process:** -``` + +```text 1. Web Build: Vite builds web assets for Electron 2. Capacitor Sync: Copies assets to Electron app directory 3. TypeScript Compile: Compiles main process code @@ -1563,7 +1775,8 @@ The `prebuild` script automatically runs before any build: ``` **Mobile Build Process:** -``` + +```text 1. Web Build: Vite builds web assets 2. Capacitor Sync: Syncs with native platforms 3. Asset Generation: Creates platform-specific assets @@ -1574,12 +1787,14 @@ The `prebuild` script automatically runs before any build: #### Service Worker Architecture **Script Organization:** + - `sw_scripts/` - Individual third-party scripts - `sw_combine.js` - Combines scripts into single file - `sw_scripts-combined.js` - Combined service worker (317KB, 10K+ lines) - `vite.config.utils.mts` - PWA configuration using combined script **PWA Integration:** + ```typescript // vite.config.utils.mts pwaConfig: { @@ -1590,31 +1805,35 @@ pwaConfig: { ``` **What Gets Combined:** + - `nacl.js` - NaCl cryptographic library - `noble-curves.js` - Elliptic curve cryptography (177KB) - `noble-hashes.js` - Cryptographic hash functions (91KB) - `safari-notifications.js` - Safari-specific notifications - `additional-scripts.js` - Additional service worker functionality -#### Environment Configuration +#### Process Environment Configuration **Environment Files:** The build system supports multiple environment file patterns: - `.env.development` - Development environment (local development) -- `.env.test` - Testing environment (staging/testing) +- `.env.test` - Testing environment (staging/testing) - `.env.production` - Production environment (production deployment) -- `.env` - General fallback environment file (loaded if mode-specific file doesn't exist) +- `.env` - General fallback environment file (loaded if mode-specific file + doesn't exist) - `.env.local` - Local development overrides (gitignored) - `.env.*.local` - Mode-specific local overrides (gitignored) **Environment Variable Precedence (Highest to Lowest):** + 1. **Shell Script Overrides** - Platform-specific overrides in build scripts 2. **Environment-Specific .env Files** - `.env.development`, `.env.test`, `.env.production` 3. **Fallback .env File** - General `.env` file (if mode-specific file doesn't exist) 4. **Hardcoded Constants** - Default values in `src/constants/app.ts` **Key Environment Variables:** + ```bash # API Servers (Environment-specific) VITE_DEFAULT_ENDORSER_API_SERVER=https://api.endorser.ch @@ -1631,13 +1850,17 @@ VITE_BVC_MEETUPS_PROJECT_CLAIM_ID=https://endorser.ch/entity/01HWE8FWHQ1YGP7GFZY ``` **Platform-Specific Overrides:** -- **Android Development**: `http://10.0.2.2:3000` (emulator) or custom IP (physical device) -- **iOS Development**: `http://localhost:3000` (simulator) or custom IP (physical device) -#### Build Output Structure +- **Android Development**: `http://10.0.2.2:3000` (emulator) or custom IP +(physical device) +- **iOS Development**: `http://localhost:3000` (simulator) or custom IP (physical +device) + +#### Build Process Output Structure **Web Build:** -``` + +```folders dist/ ├── index.html # Main HTML file ├── assets/ # Bundled JavaScript/CSS @@ -1647,7 +1870,8 @@ dist/ ``` **Electron Build:** -``` + +```folders electron/ ├── app/ # Web assets ├── build/ # Compiled TypeScript @@ -1662,35 +1886,41 @@ electron/ #### Manual vs Automatic Steps **Manual Steps (Developer Responsibility):** + - Database migration testing - Service worker script formatting (optional) - Version updates and changelog - Environment-specific builds **Automatic Steps (Build System):** + - Code linting and formatting - Script combination - Asset optimization - Package creation - Service worker injection -This architecture ensures consistent builds across all platforms while providing flexibility for platform-specific optimizations and manual quality assurance steps. +This architecture ensures consistent builds across all platforms while providing +flexibility for platform-specific optimizations and manual quality assurance steps. --- ## Appendix A: Build Scripts Reference -This appendix provides detailed documentation for all build scripts in the `scripts/` directory. +This appendix provides detailed documentation for all build scripts in the +`scripts/` directory. ### A.1 build-web.sh -**File**: `scripts/build-web.sh` -**Author**: Matthew Raymer +**File**: `scripts/build-web.sh` +**Author**: Matthew Raymer **Description**: Web build script for TimeSafari application -**Purpose**: Handles the complete web build process including cleanup, environment setup, Vite build, and optional Docker containerization. +**Purpose**: Handles the complete web build process including cleanup, +environment setup, Vite build, and optional Docker containerization. **Usage**: + ```bash # Direct script usage ./scripts/build-web.sh # Development build @@ -1714,11 +1944,13 @@ npm run build:web:docker:prod # Production Docker build ``` **Build Modes**: + - **Development**: Starts Vite dev server with hot reload (default) - **Test**: Optimized for testing with minimal minification - **Production**: Optimized for production with full minification **Script Features**: + - **Environment Validation**: Checks for Node.js, npm, npx, package.json - **Environment Setup**: Loads `.env` files based on build mode - **Clean Build**: Removes previous `dist/` directory @@ -1727,6 +1959,7 @@ npm run build:web:docker:prod # Production Docker build - **Local Serving**: Built-in HTTP server for testing builds **Exit Codes**: + - `1` - Web cleanup failed - `2` - Environment setup failed - `3` - Vite build failed @@ -1736,13 +1969,15 @@ npm run build:web:docker:prod # Production Docker build ### A.2 build-electron.sh -**File**: `scripts/build-electron.sh` -**Author**: Matthew Raymer +**File**: `scripts/build-electron.sh` +**Author**: Matthew Raymer **Description**: Clean, modular Electron build script for TimeSafari application -**Purpose**: Handles Electron builds with proper separation of concerns and no command chaining, following DRY principles. +**Purpose**: Handles Electron builds with proper separation of concerns and no +command chaining, following DRY principles. **Usage**: + ```bash # Direct script usage ./scripts/build-electron.sh # Development build (runs app) @@ -1765,22 +2000,26 @@ npm run build:web:docker:prod # Production Docker build ``` **Build Modes**: + - **Development**: Development build (runs app) - **Test**: Test environment build - **Production**: Production environment build - **Clean**: Clean Electron build artifacts only **Platforms**: + - **Windows**: Windows build - **macOS**: macOS build - **Linux**: Linux build **Packages**: + - **AppImage**: Linux AppImage - **Deb**: Debian package - **DMG**: macOS DMG **Exit Codes**: + - `1` - Invalid arguments - `2` - Electron cleanup failed - `3` - Web build failed @@ -1792,21 +2031,23 @@ npm run build:web:docker:prod # Production Docker build ### A.3 build-android.sh -**File**: `scripts/build-android.sh` -**Author**: Matthew Raymer -**Date**: 2025-07-11 +**File**: `scripts/build-android.sh` +**Author**: Matthew Raymer +**Date**: 2025-07-11 **Description**: Android build script for TimeSafari application -**Purpose**: Handles the complete Android build process including cleanup, web build, Capacitor build, Gradle build, and Android Studio launch. +**Purpose**: Handles the complete Android build process including cleanup, web + build, Capacitor build, Gradle build, and Android Studio launch. **Usage**: + ```bash # Direct script usage ./scripts/build-android.sh [options] # Options --dev, --development Build for development environment ---test Build for testing environment +--test Build for testing environment --prod, --production Build for production environment --debug Build debug APK --release Build release APK @@ -1829,15 +2070,18 @@ npm run build:web:docker:prod # Production Docker build ``` **Build Modes**: + - **Development**: Build for development environment - **Test**: Build for testing environment - **Production**: Build for production environment **Build Types**: + - **Debug**: Build debug APK (default) - **Release**: Build release APK **Exit Codes**: + - `1` - Android cleanup failed - `2` - Web build failed - `3` - Capacitor build failed @@ -1850,14 +2094,16 @@ npm run build:web:docker:prod # Production Docker build ### A.4 build-ios.sh -**File**: `scripts/build-ios.sh` -**Author**: Matthew Raymer -**Date**: 2025-07-11 +**File**: `scripts/build-ios.sh` +**Author**: Matthew Raymer +**Date**: 2025-07-11 **Description**: iOS build script for TimeSafari application -**Purpose**: Handles the complete iOS build process including cleanup, web build, Capacitor build, Xcode build, and iOS Simulator launch. +**Purpose**: Handles the complete iOS build process including cleanup, web build, + Capacitor build, Xcode build, and iOS Simulator launch. **Usage**: + ```bash # Direct script usage ./scripts/build-ios.sh [options] @@ -1888,15 +2134,18 @@ npm run build:web:docker:prod # Production Docker build ``` **Build Modes**: + - **Development**: Build for development environment - **Test**: Build for testing environment - **Production**: Build for production environment **Build Types**: + - **Debug**: Build debug app (default) - **Release**: Build release app **Key Features**: + - **Environment Validation**: Checks for Xcode, iOS Simulator, Capacitor - **Resource Checking**: Validates app icons, splash screens, Info.plist - **Clean Build**: Removes Xcode build artifacts and DerivedData @@ -1905,15 +2154,17 @@ npm run build:web:docker:prod # Production Docker build ### A.5 common.sh -**File**: `scripts/common.sh` -**Author**: Matthew Raymer +**File**: `scripts/common.sh` +**Author**: Matthew Raymer **Description**: Common utilities and functions for build scripts -**Purpose**: Provides shared functionality, logging, environment setup, and utility functions used by all build scripts. +**Purpose**: Provides shared functionality, logging, environment setup, and +utility functions used by all build scripts. **Key Functions**: **Logging Functions**: + ```bash log_info "message" # Info level logging log_success "message" # Success level logging @@ -1923,6 +2174,7 @@ log_debug "message" # Debug level logging ``` **Environment Functions**: + ```bash setup_build_env "platform" # Setup build environment for platform load_env_file "filename" # Load environment variables from file @@ -1930,6 +2182,7 @@ print_env_vars "prefix" # Print environment variables with prefix ``` **Utility Functions**: + ```bash check_command "command" # Check if command is available check_file "filename" # Check if file exists @@ -1938,6 +2191,7 @@ clean_build_artifacts "dir" # Clean build artifacts directory ``` **Validation Functions**: + ```bash validate_build_environment # Validate common build environment setup_app_directories # Setup application directories @@ -1946,13 +2200,15 @@ print_header "title" # Print formatted header ### A.6 Cleaning Commands -**File**: `package.json` scripts -**Author**: Matthew Raymer +**File**: `package.json` scripts +**Author**: Matthew Raymer **Description**: Platform-specific and comprehensive cleaning commands -**Purpose**: Provides commands to clean build artifacts for individual platforms or all platforms at once. +**Purpose**: Provides commands to clean build artifacts for individual platforms +or all platforms at once. **Available Commands**: + ```bash # Platform-specific cleaning npm run clean:ios # Clean iOS build artifacts @@ -1971,26 +2227,31 @@ npm run build:electron:clean # Clean Electron build artifacts (via build scrip **Command Details**: **clean:ios**: + - Removes iOS build directories (`ios/App/build`, `ios/App/Pods`, etc.) - Cleans DerivedData and Capacitor artifacts - Safe to run multiple times **clean:android**: + - Uninstalls app from connected devices - Cleans Android build artifacts - Safe to run multiple times **clean:electron**: + - Cleans Electron build artifacts (`electron/build`, `electron/dist`, `electron/app`) - Removes TypeScript compilation artifacts - Safe to run multiple times **clean:all**: + - Executes all platform-specific clean commands in sequence - Stops on first failure (uses `&&` operator) - Most convenient for complete cleanup **Usage Examples**: + ```bash # Clean everything before a fresh build npm run clean:all @@ -2008,17 +2269,20 @@ npm run build:web:dev ## Appendix B: Vite Configuration Files Reference -This appendix provides detailed documentation for all Vite configuration files used in the TimeSafari build system. +This appendix provides detailed documentation for all Vite configuration files used +in the TimeSafari build system. ### B.1 vite.config.common.mts -**File**: `vite.config.common.mts` -**Author**: Matthew Raymer +**File**: `vite.config.common.mts` +**Author**: Matthew Raymer **Description**: Common Vite configuration shared across all platforms -**Purpose**: Provides base configuration that is extended by platform-specific configs with unified environment handling and platform detection. +**Purpose**: Provides base configuration that is extended by platform-specific +configs with unified environment handling and platform detection. **Key Features**: + - **Platform Detection**: Automatically detects and configures for web/capacitor/electron - **Environment Setup**: Loads environment variables and sets platform flags - **Path Aliases**: Configures TypeScript path resolution and module aliases @@ -2026,6 +2290,7 @@ This appendix provides detailed documentation for all Vite configuration files u - **Dependency Management**: Handles platform-specific dependencies **Configuration Structure**: + ```typescript export async function createBuildConfig(platform: string): Promise<UserConfig> { const appConfig = await loadAppConfig(); @@ -2093,25 +2358,29 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> { ``` **Environment Variables**: + - `VITE_PLATFORM`: Set to platform name (web/capacitor/electron) - `__IS_MOBILE__`: Boolean flag for mobile platforms - `__IS_ELECTRON__`: Boolean flag for Electron platform - `__USE_QR_READER__`: Boolean flag for QR reader availability **Path Aliases**: + - `@`: Points to `src/` directory - `@nostr/tools`: Nostr tools library - `path`, `fs`, `crypto`: Node.js polyfills for browser ### B.2 vite.config.web.mts -**File**: `vite.config.web.mts` -**Author**: Matthew Raymer +**File**: `vite.config.web.mts` +**Author**: Matthew Raymer **Description**: Vite configuration for web platform with PWA support -**Purpose**: Configures Vite for web builds with environment-specific optimizations and PWA features. +**Purpose**: Configures Vite for web builds with environment-specific +optimizations and PWA features. **Key Features**: + - **Environment-Specific Configuration**: Different settings for dev/test/prod - **PWA Integration**: Progressive Web App support with service worker - **Build Optimization**: Manual chunk splitting for better caching @@ -2121,6 +2390,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> { **Environment Configurations**: **Development Mode**: + ```typescript { build: { @@ -2137,6 +2407,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> { ``` **Test Mode**: + ```typescript { build: { @@ -2153,6 +2424,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> { ``` **Production Mode**: + ```typescript { build: { @@ -2178,6 +2450,7 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> { ``` **PWA Configuration**: + ```typescript VitePWA({ registerType: 'autoUpdate', @@ -2196,13 +2469,15 @@ VitePWA({ ### B.3 vite.config.electron.mts -**File**: `vite.config.electron.mts` -**Author**: Matthew Raymer +**File**: `vite.config.electron.mts` +**Author**: Matthew Raymer **Description**: Vite configuration for Electron desktop platform -**Purpose**: Configures Vite for Electron builds with desktop-specific optimizations and native module support. +**Purpose**: Configures Vite for Electron builds with desktop-specific optimizations + and native module support. **Key Features**: + - **Electron-Specific Entry Point**: Uses `main.electron.ts` instead of `main.web.ts` - **Native Module Support**: Handles Electron-specific dependencies - **Desktop Optimizations**: Larger chunk sizes and desktop-specific settings @@ -2210,10 +2485,11 @@ VitePWA({ - **External Dependencies**: Properly handles Electron and native modules **Configuration Structure**: + ```typescript export default defineConfig(async () => { const baseConfig = await createBuildConfig("electron"); - + return { ...baseConfig, plugins: [ @@ -2247,24 +2523,28 @@ export default defineConfig(async () => { ``` **Plugins**: + - **electron-entry-point**: Replaces main entry point for Electron - **electron-config**: Handles Electron-specific configurations - **suppress-source-maps**: Suppresses source map loading errors **External Dependencies**: + - `electron`: Electron runtime - `@capacitor-community/electron`: Capacitor Electron plugin - `better-sqlite3-multiple-ciphers`: Native SQLite module ### B.4 vite.config.capacitor.mts -**File**: `vite.config.capacitor.mts` -**Author**: Matthew Raymer +**File**: `vite.config.capacitor.mts` +**Author**: Matthew Raymer **Description**: Vite configuration for Capacitor mobile platform -**Purpose**: Provides minimal configuration for Capacitor builds, inheriting from common config. +**Purpose**: Provides minimal configuration for Capacitor builds, inheriting from +common config. **Configuration**: + ```typescript import { defineConfig } from "vite"; import { createBuildConfig } from "./vite.config.common.mts"; @@ -2273,6 +2553,7 @@ export default defineConfig(async () => createBuildConfig('capacitor')); ``` **Key Features**: + - **Minimal Configuration**: Inherits all settings from common config - **Mobile Platform**: Automatically configures for mobile-specific settings - **PWA Disabled**: Progressive Web App features disabled for native apps @@ -2280,8 +2561,8 @@ export default defineConfig(async () => createBuildConfig('capacitor')); ### B.5 vite.config.utils.mts -**File**: `vite.config.utils.mts` -**Author**: Matthew Raymer +**File**: `vite.config.utils.mts` +**Author**: Matthew Raymer **Description**: Utility functions for Vite configuration **Purpose**: Provides shared configuration loading and PWA manifest generation. @@ -2293,6 +2574,7 @@ export default defineConfig(async () => createBuildConfig('capacitor')); **loadAppConfig()**: Loads complete application configuration including PWA settings **PWA Configuration**: + ```typescript interface PWAConfig { registerType: string; @@ -2311,11 +2593,13 @@ interface PWAConfig { ``` **Manifest Icons**: + - Android Chrome 192x192 PNG - Android Chrome 512x512 PNG - Maskable icons for adaptive UI **Share Target Configuration**: + ```typescript share_target: { action: "/share-target", @@ -2328,6 +2612,7 @@ share_target: { ``` **Alias Configuration**: + - `@`: Source directory alias - `buffer`: Buffer polyfill - `dexie-export-import`: Database import/export utilities @@ -2362,23 +2647,27 @@ source "$(dirname "$0")/common.sh" ### C.3 Build Process Flow **Web Build**: -``` + +```text build-web.sh → vite.config.web.mts → dist/ ``` **Electron Build**: -``` + +```text build-electron.sh → vite.config.electron.mts → electron/app/ ``` **Mobile Build**: -``` + +```text build-android.sh/build-ios.sh → vite.config.capacitor.mts → android/ios/ ``` ### C.4 Error Handling All scripts use consistent error handling: + - Exit codes for different failure types - Verbose logging with `--verbose` flag - Safe command execution with `safe_execute()` @@ -2387,20 +2676,138 @@ All scripts use consistent error handling: ### C.5 Platform-Specific Considerations **Web Platform**: + - PWA features enabled - Service worker injection - Browser-specific optimizations **Electron Platform**: + - Native module support - Desktop-specific entry points - Source map suppression **Mobile Platform**: + - Capacitor integration - Native asset generation - Platform-specific builds --- -**Note**: This documentation is maintained alongside the build system. For the most up-to-date information, refer to the actual script files and Vite configuration files in the repository. +**Note**: This documentation is maintained alongside the build system. For the +most up-to-date information, refer to the actual script files and Vite +configuration files in the repository. + +--- + +## Build Changes Changelog + +### 2025-08-21 - Cursor Rules Refactoring and Build System Updates + +#### Package Dependencies Updated +- **Added**: `markdownlint-cli2` v0.18.1 - Modern markdown linting with improved performance +- **Added**: `@commitlint/cli` v18.6.1 - Conventional commit message validation +- **Added**: `@commitlint/config-conventional` v18.6.2 - Conventional commit standards +- **Updated**: `husky` v9.0.11 - Git hooks management +- **Updated**: `lint-staged` v15.2.2 - Pre-commit linting automation + +#### Build Script Improvements +- **Markdown Linting**: Replaced custom markdown scripts with `markdownlint-cli2` + - **Before**: `./scripts/fix-markdown.sh` and `./scripts/validate-markdown.sh` + - **After**: `markdownlint-cli2 --fix` and `markdownlint-cli2` + - **Benefits**: Faster execution, better error reporting, modern markdown standards + +#### Lint-Staged Configuration Enhanced +- **Added**: Markdown file linting to pre-commit hooks + - **Pattern**: `*.{md,markdown,mdc}` files now automatically formatted + - **Command**: `markdownlint-cli2 --fix` runs before each commit + - **Coverage**: All markdown files including `.mdc` cursor rules + +#### Commit Message Standards +- **Added**: Conventional commit validation via commitlint +- **Configuration**: Extends `@commitlint/config-conventional` +- **Enforcement**: Ensures consistent commit message format across the project + +#### Node.js Version Requirements +- **Updated**: Minimum Node.js version requirements for new dependencies +- **markdownlint-cli2**: Requires Node.js >=20 +- **Various utilities**: Require Node.js >=18 for modern ES features + +#### Build Process Impact +- **No Breaking Changes**: All existing build commands continue to work +- **Improved Quality**: Better markdown formatting and commit message standards +- **Enhanced Automation**: More comprehensive pre-commit validation +- **Performance**: Faster markdown linting with modern tooling + +--- + +### 2025-08-21 - Commitlint Configuration Refinement + +#### Commit Message Validation Improvements +- **Modified**: Commitlint configuration moved from `package.json` to dedicated `commitlint.config.js` +- **Enhanced**: Strict validation rules downgraded from errors to warnings + - **Before**: `subject-case` and `subject-full-stop` rules caused red error messages + - **After**: Same rules now show yellow warnings without blocking commits +- **Benefit**: Eliminates confusing red error messages while maintaining commit quality guidance + +#### Configuration Structure +- **File**: `commitlint.config.js` - Dedicated commitlint configuration +- **Extends**: `@commitlint/config-conventional` - Standard conventional commit rules +- **Custom Rules**: + - `subject-case: [1, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']]` + - `subject-full-stop: [1, 'never', '.']` +- **Levels**: + - `0` = Disabled, `1` = Warning, `2` = Error + - Current: Problematic rules set to warning level (1) + +#### User Experience Impact +- **Before**: Red error messages on every push with strict commit rules +- **After**: Yellow warning messages that provide guidance without disruption +- **Workflow**: Commits and pushes continue to work while maintaining quality standards +- **Feedback**: Developers still receive helpful commit message guidance + +--- + +### 2025-08-26 - Capacitor Plugin Additions + +#### New Capacitor Plugins Added +- **Added**: `@capacitor/clipboard` v6.0.2 - Clipboard functionality for mobile platforms + - **Purpose**: Enable copy/paste operations on mobile devices + - **Platforms**: iOS and Android + - **Features**: Read/write clipboard content, text handling + - **Integration**: Automatically included in mobile builds + +- **Added**: `@capacitor/status-bar` v6.0.2 - Status bar management for mobile platforms + - **Purpose**: Control mobile device status bar appearance and behavior + - **Platforms**: iOS and Android + - **Features**: Status bar styling, visibility control, color management + - **Integration**: Automatically included in mobile builds + +#### Android Build System Updates +- **Modified**: `android/capacitor.settings.gradle` - Added new plugin project includes + - **Added**: `:capacitor-clipboard` project directory mapping + - **Added**: `:capacitor-status-bar` project directory mapping + - **Impact**: New plugins now properly integrated into Android build process + +#### Package Dependencies +- **Updated**: `package.json` - Added new Capacitor plugin dependencies +- **Updated**: `package-lock.json` - Locked dependency versions for consistency +- **Version**: All new plugins use Capacitor 6.x compatible versions + +#### Build Process Impact +- **No Breaking Changes**: Existing build commands continue to work unchanged +- **Enhanced Mobile Features**: New clipboard and status bar capabilities available +- **Automatic Integration**: Plugins automatically included in mobile builds +- **Platform Support**: Both iOS and Android builds now include new functionality + +#### Testing Requirements +- **Mobile Builds**: Verify new plugins integrate correctly in iOS and Android builds +- **Functionality**: Test clipboard operations and status bar management on devices +- **Fallback**: Ensure graceful degradation when plugins are unavailable + +--- + +**Note**: This documentation is maintained alongside the build system. For the +most up-to-date information, refer to the actual script files and Vite +configuration files in the repository. diff --git a/CHANGELOG.md b/CHANGELOG.md index cf28e788..641ff920 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,72 +5,89 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.0.3] - 2025.07.12 -### Changed -- Photo is pinned to profile mode +## [1.0.7] - 2025.08.18 + ### Fixed -- Deep link URLs (and other prod settings) -- Error in BVC begin view + +- Deep link for onboard-meeting-members ## [1.0.6] - 2025.08.09 + ### Fixed -- Deep link errors where none would validate +- Deep link errors where none would validate ## [1.0.5] - 2025.07.24 + ### Fixed -- Export & import of contacts corrupted contact methods +- Export & import of contacts corrupted contact methods ## [1.0.4] - 2025.07.20 - 002f2407208d56cc59c0aa7c880535ae4cbace8b + ### Fixed -- Deep link for invite-one-accept +- Deep link for invite-one-accept ## [1.0.3] - 2025.07.12 - a9a8ba217cd6015321911e98e6843e988dc2c4ae + ### Changed + - Photo is pinned to profile mode + ### Fixed + - Deep link URLs (and other prod settings) - Error in BVC begin view - ## [1.0.2] - 2025.06.20 - 276e0a741bc327de3380c4e508cccb7fee58c06d + ### Added -- Version on feed title +- Version on feed title ## [1.0.1] - 2025.06.20 + ### Added -- Allow a user to block someone else's content from view +- Allow a user to block someone else's content from view ## [1.0.0] - 2025.06.20 - 5aa693de6337e5dbb278bfddc6bd39094bc14f73 + ### Added -- Web-oriented migration from IndexedDB to SQLite +- Web-oriented migration from IndexedDB to SQLite ## [0.5.8] + ### Added + - /deep-link/ path for URLs that are shared with people + ### Changed + - External links now go to /deep-link/... - Feed visuals now have arrow imagery from giver to receiver - ## [0.4.7] + ### Fixed + - Cameras everywhere + ### Changed -- IndexedDB -> SQLite +- IndexedDB -> SQLite ## [0.4.5] - 2025.02.23 + ### Added + - Total amounts of gives on project page + ### Changed in DB or environment -- Requires Endorser.ch version 4.2.6+ +- Requires Endorser.ch version 4.2.6+ ## [0.4.4] - 2025.02.17 diff --git a/README-PR-TEMPLATE.md b/README-PR-TEMPLATE.md new file mode 100644 index 00000000..fa977e02 --- /dev/null +++ b/README-PR-TEMPLATE.md @@ -0,0 +1,82 @@ +# Pull Request Template + +## Location + +The Build Architecture Guard PR template is located at: + +- **`pull_request_template.md`** (root directory) + +## Usage + +When creating a pull request in Gitea, this template will automatically populate the PR description with the required checklist. + +## Template Features + +### Change Level Classification + +- **L1**: Minor changes, documentation updates +- **L2**: Moderate changes, new features, environment changes +- **L3**: Major changes, architecture changes, new platforms + +### Required Fields for All Levels + +- Change level selection +- Scope and impact description +- Commands executed and their output +- Documentation updates (BUILDING.md) +- Rollback verification steps + +### Additional Requirements for L3 + +- **ADR link**: Must provide URL to Architectural Decision Record +- **Artifacts with SHA256**: Must list artifacts with cryptographic hashes + +## Integration + +This template works with: + +- **Gitea Actions**: `.gitea/workflows/build-guard.yml` +- **Client-side hooks**: `.husky/` pre-commit and pre-push hooks +- **Guard script**: `scripts/build-arch-guard.sh` + +## Example Usage + +```markdown +### Change Level +- [x] Level: **L2** + +**Why:** Adding new build script for Docker deployment + +### Scope & Impact +- [x] Files & platforms touched: scripts/build-docker.sh, + BUILDING.md +- [x] Risk triggers: Docker build process changes +- [x] Mitigations/validation done: Tested on local Docker environment + +### Commands Run +- [x] Web: `npm run build:web:docker` ✅ +- [x] Docker: `docker build -t test-image .` ✅ + +### Artifacts +- [x] Names + **sha256** of artifacts/installers: + +Artifacts: +```text +test-image.tar a1b2c3d4e5f6... +``` + +### Docs +- [x] **BUILDING.md** updated (sections): Docker deployment +- [x] Troubleshooting updated: Added Docker troubleshooting section + +### Rollback +- [x] Verified steps to restore previous behavior: + 1. `git revert HEAD` + 2. `docker rmi test-image` + 3. Restore previous BUILDING.md +``` + +--- + +**Note**: This template is enforced by the Build Architecture Guard +system. Complete all required fields to ensure your PR can be merged. diff --git a/README.md b/README.md index 6992ba09..cf01f3bf 100644 --- a/README.md +++ b/README.md @@ -18,10 +18,44 @@ npm install npm run build:web:serve -- --test ``` -To be able to make submissions: go to "profile" (bottom left), go to the bottom and expand "Show Advanced Settings", go to the bottom and to the "Test Page", and finally "Become User 0" to see all the functionality. +To be able to take action on the platform: go to [the test page](http://localhost:8080/test) and click "Become User 0". See [BUILDING.md](BUILDING.md) for comprehensive build instructions for all platforms (Web, Electron, iOS, Android, Docker). +## 🛡️ Build Architecture Guard + +This project uses **Husky Git hooks** to protect the build system +architecture. When you modify build-critical files, the system +automatically blocks commits until you update `BUILDING.md`. + +### Quick Setup + +```bash +npm run guard:setup # Install and activate the guard +``` + +### How It Works + +- **Pre-commit**: Blocks commits if build files changed without + BUILDING.md updates +- **Pre-push**: Blocks pushes if commits contain undocumented build + changes +- **Protected paths**: `scripts/`, `vite.config.*`, `electron/`, + `android/`, `ios/`, etc. + +### Usage + +```bash +# Test the guard manually +npm run guard:test + +# Emergency bypass (use sparingly) +git commit --no-verify +git push --no-verify +``` + +**📚 Full documentation**: See `doc/README-BUILD-GUARD.md` + ## Development Database Clearing TimeSafari provides a simple script-based approach to clear the local database (not the claim server) for development purposes. @@ -136,11 +170,77 @@ const apiUrl = `${APP_SERVER}/api/claim/123`; See [TESTING.md](test-playwright/TESTING.md) for detailed test instructions. -## Icons +## Asset Management + +TimeSafari uses a standardized asset configuration system for consistent +icon and splash screen generation across all platforms. + +### Asset Sources + +- **Single source of truth**: `resources/` directory (Capacitor default) +- **Source files**: `icon.png`, `splash.png`, `splash_dark.png` +- **Format**: PNG or SVG files for optimal quality + +### Asset Generation + +- **Configuration**: `config/assets/capacitor-assets.config.json` +- **Schema validation**: `config/assets/schema.json` +- **Build-time generation**: Platform assets generated via `capacitor-assets` +- **No VCS commits**: Generated assets are never committed to version control + +### Development Commands + +```bash +# Generate/update asset configurations +npm run assets:config -Application icons are in the `assets` directory, processed by the `capacitor-assets` command. +# Validate asset configurations +npm run assets:validate -To add a Font Awesome icon, add to fontawesome.ts and reference with `font-awesome` element and `icon` attribute with the hyphenated name. +# Clean generated platform assets (local dev only) +npm run assets:clean + +# Build with asset generation +npm run build:native +``` + +### Environment Setup & Dependencies + +Before building the application, ensure your development environment is properly +configured: + +```bash +# Install all dependencies (required first time and after updates) +npm install + +# Validate your development environment +npm run check:dependencies + +# Check prerequisites for testing +npm run test:prerequisites +``` + +**Common Issues & Solutions**: + +- **"tsx: command not found"**: Run `npm install` to install devDependencies +- **"capacitor-assets: command not found"**: Ensure `@capacitor/assets` is installed +- **Build failures**: Run `npm run check:dependencies` to diagnose environment issues + +**Required Versions**: +- Node.js: 18+ (LTS recommended) +- npm: 8+ (comes with Node.js) +- Platform-specific tools: Android Studio, Xcode (for mobile builds) + +### Platform Support + +- **Android**: Adaptive icons with foreground/background, monochrome support +- **iOS**: LaunchScreen storyboard preferred, splash assets when needed +- **Web**: PWA icons generated during build to `dist/` (not committed) + +### Font Awesome Icons + +To add a Font Awesome icon, add to `fontawesome.ts` and reference with +`font-awesome` element and `icon` attribute with the hyphenated name. ## Other @@ -190,7 +290,29 @@ The application uses a platform-agnostic database layer with Vue mixins for serv **Architecture Decision**: The project uses Vue mixins over Composition API composables for platform service access. See [Architecture Decisions](doc/architecture-decisions.md) for detailed rationale. -### Kudos +## 📁 Project Structure + +```text +timesafari/ +├── 📁 src/ # Source code +├── 📁 scripts/ # Build and automation scripts +├── 📁 electron/ # Electron configuration +├── 📁 android/ # Android configuration +├── 📁 ios/ # iOS configuration +├── 📁 .husky/ # Git hooks (Build Architecture Guard) +├── 📄 BUILDING.md # Build system documentation +├── 📄 pull_request_template.md # PR template +└── 📄 doc/README-BUILD-GUARD.md # Guard system documentation +``` + +## 🤝 Contributing + +1. **Follow the Build Architecture Guard** - Update BUILDING.md when modifying build files +2. **Use the PR template** - Complete the checklist for build-related changes +3. **Test your changes** - Ensure builds work on affected platforms +4. **Document updates** - Keep BUILDING.md current and accurate + +## Kudos Gifts make the world go 'round! diff --git a/TASK_storage.md b/TASK_storage.md index a33cbb87..52b52b76 100644 --- a/TASK_storage.md +++ b/TASK_storage.md @@ -1,7 +1,6 @@ # What to do about storage for native apps? - ## Problem We can't trust iOS IndexedDB to persist. I want to start delivering an app to people now, in preparation for presentations mid-June: Rotary on June 12 and Porcfest on June 17. @@ -14,7 +13,6 @@ We can't trust iOS IndexedDB to persist. I want to start delivering an app to pe Also, with sensitive data, the accounts info should be encrypted. - # Options * There is a community [SQLite plugin for Capacitor](https://github.com/capacitor-community/sqlite) with encryption by [SQLCipher](https://github.com/sqlcipher/sqlcipher). @@ -29,16 +27,12 @@ Also, with sensitive data, the accounts info should be encrypted. * Not an option yet: Dexie may support SQLite in [a future version](https://dexie.org/roadmap/dexie5.0). - - # Current Plan * Implement SQLite for Capacitor & web, with encryption. That will allow us to test quickly and keep the same interface for native & web, but we don't deal with migrations for current web users. * After that is delivered, write a migration for current web users from IndexedDB to SQLite. - - # Current method calls ... which is not 100% complete because the AI that generated thus claimed no usage of 'temp' DB. @@ -80,5 +74,3 @@ Logs operations: db.logs.get(todayKey) - Gets logs for a specific day db.logs.update(todayKey, { message: fullMessage }) - Updates logs db.logs.clear() - Clears all logs - - diff --git a/android/app/build.gradle b/android/app/build.gradle index a92af2db..57c34006 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -31,8 +31,8 @@ android { applicationId "app.timesafari.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 39 - versionName "1.0.6" + versionCode 40 + versionName "1.0.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/android/app/capacitor.build.gradle b/android/app/capacitor.build.gradle index ec740be6..f1774d9f 100644 --- a/android/app/capacitor.build.gradle +++ b/android/app/capacitor.build.gradle @@ -13,8 +13,10 @@ dependencies { implementation project(':capacitor-mlkit-barcode-scanning') implementation project(':capacitor-app') implementation project(':capacitor-camera') + implementation project(':capacitor-clipboard') implementation project(':capacitor-filesystem') implementation project(':capacitor-share') + implementation project(':capacitor-status-bar') implementation project(':capawesome-capacitor-file-picker') } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 77641501..1d8ad70d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -14,6 +14,7 @@ android:label="@string/title_activity_main" android:launchMode="singleTask" android:screenOrientation="portrait" + android:windowSoftInputMode="adjustResize" android:theme="@style/AppTheme.NoActionBarLaunch"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/android/app/src/main/assets/capacitor.config.json b/android/app/src/main/assets/capacitor.config.json index 594ebca3..04ab8d0c 100644 --- a/android/app/src/main/assets/capacitor.config.json +++ b/android/app/src/main/assets/capacitor.config.json @@ -29,7 +29,7 @@ "splashFullScreen": true, "splashImmersive": true }, - "CapacitorSQLite": { + "CapSQLite": { "iosDatabaseLocation": "Library/CapacitorDatabase", "iosIsEncryption": false, "iosBiometric": { diff --git a/android/app/src/main/assets/capacitor.plugins.json b/android/app/src/main/assets/capacitor.plugins.json index a95bd42f..72f18d8c 100644 --- a/android/app/src/main/assets/capacitor.plugins.json +++ b/android/app/src/main/assets/capacitor.plugins.json @@ -15,6 +15,10 @@ "pkg": "@capacitor/camera", "classpath": "com.capacitorjs.plugins.camera.CameraPlugin" }, + { + "pkg": "@capacitor/clipboard", + "classpath": "com.capacitorjs.plugins.clipboard.ClipboardPlugin" + }, { "pkg": "@capacitor/filesystem", "classpath": "com.capacitorjs.plugins.filesystem.FilesystemPlugin" @@ -23,6 +27,10 @@ "pkg": "@capacitor/share", "classpath": "com.capacitorjs.plugins.share.SharePlugin" }, + { + "pkg": "@capacitor/status-bar", + "classpath": "com.capacitorjs.plugins.statusbar.StatusBarPlugin" + }, { "pkg": "@capawesome/capacitor-file-picker", "classpath": "io.capawesome.capacitorjs.plugins.filepicker.FilePickerPlugin" diff --git a/android/app/src/main/java/app/timesafari/MainActivity.java b/android/app/src/main/java/app/timesafari/MainActivity.java index 12429d63..29224f45 100644 --- a/android/app/src/main/java/app/timesafari/MainActivity.java +++ b/android/app/src/main/java/app/timesafari/MainActivity.java @@ -1,7 +1,16 @@ package app.timesafari; import android.os.Bundle; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowInsetsController; +import android.view.WindowInsets; +import android.os.Build; +import android.webkit.WebView; +import android.webkit.WebSettings; +import android.webkit.WebViewClient; import com.getcapacitor.BridgeActivity; +import app.timesafari.safearea.SafeAreaPlugin; //import com.getcapacitor.community.sqlite.SQLite; public class MainActivity extends BridgeActivity { @@ -9,7 +18,39 @@ public class MainActivity extends BridgeActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // Enable edge-to-edge display for modern Android + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Android 11+ (API 30+) + getWindow().setDecorFitsSystemWindows(false); + + // Set up system UI visibility for edge-to-edge + WindowInsetsController controller = getWindow().getInsetsController(); + if (controller != null) { + controller.setSystemBarsAppearance( + WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS | + WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS, + WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS | + WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS + ); + controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + } else { + // Legacy Android (API 21-29) + getWindow().getDecorView().setSystemUiVisibility( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE | + View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | + View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | + View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + ); + } + + // Register SafeArea plugin + registerPlugin(SafeAreaPlugin.class); + // Initialize SQLite //registerPlugin(SQLite.class); } + + } \ No newline at end of file diff --git a/android/app/src/main/java/app/timesafari/safearea/SafeAreaPlugin.java b/android/app/src/main/java/app/timesafari/safearea/SafeAreaPlugin.java new file mode 100644 index 00000000..cb433d57 --- /dev/null +++ b/android/app/src/main/java/app/timesafari/safearea/SafeAreaPlugin.java @@ -0,0 +1,44 @@ +package app.timesafari.safearea; + +import android.os.Build; +import android.view.WindowInsets; +import com.getcapacitor.JSObject; +import com.getcapacitor.Plugin; +import com.getcapacitor.PluginCall; +import com.getcapacitor.PluginMethod; +import com.getcapacitor.annotation.CapacitorPlugin; + +@CapacitorPlugin(name = "SafeArea") +public class SafeAreaPlugin extends Plugin { + + @PluginMethod + public void getSafeAreaInsets(PluginCall call) { + JSObject result = new JSObject(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + WindowInsets insets = getActivity().getWindow().getDecorView().getRootWindowInsets(); + if (insets != null) { + int top = insets.getInsets(WindowInsets.Type.statusBars()).top; + int bottom = insets.getInsets(WindowInsets.Type.navigationBars()).bottom; + int left = insets.getInsets(WindowInsets.Type.systemBars()).left; + int right = insets.getInsets(WindowInsets.Type.systemBars()).right; + + result.put("top", top); + result.put("bottom", bottom); + result.put("left", left); + result.put("right", right); + + call.resolve(result); + return; + } + } + + // Fallback values + result.put("top", 0); + result.put("bottom", 0); + result.put("left", 0); + result.put("right", 0); + + call.resolve(result); + } +} diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml new file mode 100644 index 00000000..79f802e1 --- /dev/null +++ b/android/app/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="colorPrimary">#3F51B5</color> + <color name="colorPrimaryDark">#303F9F</color> + <color name="colorAccent">#FF4081</color> + <color name="ic_launcher_background">#FFFFFF</color> +</resources> diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index be874e54..6a1ec3ca 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -18,5 +18,14 @@ <style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen"> <item name="android:background">@drawable/splash</item> + <item name="android:windowTranslucentStatus">false</item> + <item name="android:windowTranslucentNavigation">false</item> + <item name="android:windowDrawsSystemBarBackgrounds">true</item> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:navigationBarColor">@android:color/transparent</item> + <item name="android:windowLightStatusBar">true</item> + <item name="android:windowLightNavigationBar">true</item> + <item name="android:enforceStatusBarContrast">false</item> + <item name="android:enforceNavigationBarContrast">false</item> </style> </resources> \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 858b0bc0..4bd6375d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.12.0' + classpath 'com.android.tools.build:gradle:8.12.1' classpath 'com.google.gms:google-services:4.4.0' // NOTE: Do not place your application dependencies here; they belong diff --git a/android/capacitor.settings.gradle b/android/capacitor.settings.gradle index 3c06dfe7..891b5455 100644 --- a/android/capacitor.settings.gradle +++ b/android/capacitor.settings.gradle @@ -14,11 +14,17 @@ project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/ include ':capacitor-camera' project(':capacitor-camera').projectDir = new File('../node_modules/@capacitor/camera/android') +include ':capacitor-clipboard' +project(':capacitor-clipboard').projectDir = new File('../node_modules/@capacitor/clipboard/android') + include ':capacitor-filesystem' project(':capacitor-filesystem').projectDir = new File('../node_modules/@capacitor/filesystem/android') include ':capacitor-share' project(':capacitor-share').projectDir = new File('../node_modules/@capacitor/share/android') +include ':capacitor-status-bar' +project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android') + include ':capawesome-capacitor-file-picker' project(':capawesome-capacitor-file-picker').projectDir = new File('../node_modules/@capawesome/capacitor-file-picker/android') diff --git a/assets/README.md b/assets/README.md deleted file mode 100644 index b9272ff0..00000000 --- a/assets/README.md +++ /dev/null @@ -1,2 +0,0 @@ - -Application icons are here. They are processed for android & ios by the `capacitor-assets` command, as indicated in the BUILDING.md file. diff --git a/capacitor-assets.config.json b/capacitor-assets.config.json index d56533f4..92bd0414 100644 --- a/capacitor-assets.config.json +++ b/capacitor-assets.config.json @@ -1,36 +1,32 @@ { "icon": { - "ios": { - "source": "resources/ios/icon/icon.png", - "target": "ios/App/App/Assets.xcassets/AppIcon.appiconset" - }, "android": { - "source": "resources/android/icon/icon.png", + "adaptive": { + "background": "#121212", + "foreground": "resources/icon.png", + "monochrome": "resources/icon.png" + }, "target": "android/app/src/main/res" }, + "ios": { + "padding": 0, + "target": "ios/App/App/Assets.xcassets/AppIcon.appiconset" + }, + "source": "resources/icon.png", "web": { - "source": "resources/web/icon/icon.png", "target": "public/img/icons" } }, "splash": { - "ios": { - "source": "resources/ios/splash/splash.png", - "target": "ios/App/App/Assets.xcassets/Splash.imageset" - }, "android": { - "source": "resources/android/splash/splash.png", + "scale": "cover", "target": "android/app/src/main/res" - } - }, - "splashDark": { + }, + "darkSource": "resources/splash_dark.png", "ios": { - "source": "resources/ios/splash/splash_dark.png", - "target": "ios/App/App/Assets.xcassets/SplashDark.imageset" + "target": "ios/App/App/Assets.xcassets", + "useStoryBoard": true }, - "android": { - "source": "resources/android/splash/splash_dark.png", - "target": "android/app/src/main/res" - } + "source": "resources/splash.png" } -} \ No newline at end of file +} diff --git a/capacitor.config.ts b/capacitor.config.ts new file mode 100644 index 00000000..24ef38c6 --- /dev/null +++ b/capacitor.config.ts @@ -0,0 +1,116 @@ +import { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'app.timesafari', + appName: 'TimeSafari', + webDir: 'dist', + server: { + cleartext: true + }, + plugins: { + App: { + appUrlOpen: { + handlers: [ + { + url: 'timesafari://*', + autoVerify: true + } + ] + } + }, + SplashScreen: { + launchShowDuration: 3000, + launchAutoHide: true, + backgroundColor: '#ffffff', + androidSplashResourceName: 'splash', + androidScaleType: 'CENTER_CROP', + showSpinner: false, + androidSpinnerStyle: 'large', + iosSpinnerStyle: 'small', + spinnerColor: '#999999', + splashFullScreen: true, + splashImmersive: true + }, + CapSQLite: { + iosDatabaseLocation: 'Library/CapacitorDatabase', + iosIsEncryption: false, + iosBiometric: { + biometricAuth: false, + biometricTitle: 'Biometric login for TimeSafari' + }, + androidIsEncryption: false, + androidBiometric: { + biometricAuth: false, + biometricTitle: 'Biometric login for TimeSafari' + }, + electronIsEncryption: false + } + }, + ios: { + contentInset: 'never', + allowsLinkPreview: true, + scrollEnabled: true, + limitsNavigationsToAppBoundDomains: true, + backgroundColor: '#ffffff', + allowNavigation: [ + '*.timesafari.app', + '*.jsdelivr.net', + 'api.endorser.ch' + ] + }, + android: { + allowMixedContent: true, + captureInput: true, + webContentsDebuggingEnabled: false, + allowNavigation: [ + '*.timesafari.app', + '*.jsdelivr.net', + 'api.endorser.ch', + '10.0.2.2:3000' + ] + }, + electron: { + deepLinking: { + schemes: ['timesafari'] + }, + buildOptions: { + appId: 'app.timesafari', + productName: 'TimeSafari', + directories: { + output: 'dist-electron-packages' + }, + files: [ + 'dist/**/*', + 'electron/**/*' + ], + mac: { + category: 'public.app-category.productivity', + target: [ + { + target: 'dmg', + arch: ['x64', 'arm64'] + } + ] + }, + win: { + target: [ + { + target: 'nsis', + arch: ['x64'] + } + ] + }, + linux: { + target: [ + { + target: 'AppImage', + arch: ['x64'] + } + ], + category: 'Utility' + } + } + } +}; + +export default config; diff --git a/commitlint.config.js b/commitlint.config.js new file mode 100644 index 00000000..89dec502 --- /dev/null +++ b/commitlint.config.js @@ -0,0 +1,9 @@ +module.exports = { + extends: ['@commitlint/config-conventional'], + rules: { + // Downgrade strict case rules to warnings (level 1) instead of errors (level 2) + // This eliminates red error messages while maintaining helpful guidance + 'subject-case': [1, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']], + 'subject-full-stop': [1, 'never', '.'], + } +}; diff --git a/config/assets/capacitor-assets.config.json b/config/assets/capacitor-assets.config.json new file mode 100644 index 00000000..92bd0414 --- /dev/null +++ b/config/assets/capacitor-assets.config.json @@ -0,0 +1,32 @@ +{ + "icon": { + "android": { + "adaptive": { + "background": "#121212", + "foreground": "resources/icon.png", + "monochrome": "resources/icon.png" + }, + "target": "android/app/src/main/res" + }, + "ios": { + "padding": 0, + "target": "ios/App/App/Assets.xcassets/AppIcon.appiconset" + }, + "source": "resources/icon.png", + "web": { + "target": "public/img/icons" + } + }, + "splash": { + "android": { + "scale": "cover", + "target": "android/app/src/main/res" + }, + "darkSource": "resources/splash_dark.png", + "ios": { + "target": "ios/App/App/Assets.xcassets", + "useStoryBoard": true + }, + "source": "resources/splash.png" + } +} diff --git a/config/assets/schema.json b/config/assets/schema.json new file mode 100644 index 00000000..89e76c0c --- /dev/null +++ b/config/assets/schema.json @@ -0,0 +1,119 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Capacitor Assets Configuration Schema", + "description": "Schema for validating capacitor-assets configuration files", + "type": "object", + "properties": { + "icon": { + "type": "object", + "properties": { + "source": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Source icon file path relative to project root" + }, + "android": { + "type": "object", + "properties": { + "adaptive": { + "type": "object", + "properties": { + "foreground": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Foreground icon for Android adaptive icons" + }, + "background": { + "type": ["string", "object"], + "description": "Background color or image for adaptive icons" + }, + "monochrome": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Monochrome icon for Android 13+" + } + }, + "required": ["foreground", "background"] + }, + "target": { + "type": "string", + "description": "Android target directory for generated icons" + } + } + }, + "ios": { + "type": "object", + "properties": { + "padding": { + "type": "number", + "minimum": 0, + "maximum": 1, + "description": "Padding ratio for iOS icons" + }, + "target": { + "type": "string", + "description": "iOS target directory for generated icons" + } + } + }, + "web": { + "type": "object", + "properties": { + "target": { + "type": "string", + "description": "Web target directory for generated icons" + } + } + } + }, + "required": ["source"], + "additionalProperties": false + }, + "splash": { + "type": "object", + "properties": { + "source": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Source splash screen file" + }, + "darkSource": { + "type": "string", + "pattern": "^resources/.*\\.(png|svg)$", + "description": "Dark mode splash screen file" + }, + "android": { + "type": "object", + "properties": { + "scale": { + "type": "string", + "enum": ["cover", "contain", "fill"], + "description": "Android splash screen scaling mode" + }, + "target": { + "type": "string", + "description": "Android target directory for splash screens" + } + } + }, + "ios": { + "type": "object", + "properties": { + "useStoryBoard": { + "type": "boolean", + "description": "Use LaunchScreen storyboard instead of splash assets" + }, + "target": { + "type": "string", + "description": "iOS target directory for splash screens" + } + } + } + }, + "required": ["source"], + "additionalProperties": false + } + }, + "required": ["icon", "splash"], + "additionalProperties": false +} diff --git a/doc/DEEP_LINKS.md b/doc/DEEP_LINKS.md index a6bf9f6b..8638074b 100644 --- a/doc/DEEP_LINKS.md +++ b/doc/DEEP_LINKS.md @@ -47,6 +47,7 @@ type ClaimParams = z.infer<typeof claimSchema>; ### Type Safety Layers 1. **Schema Definition** + ```typescript // src/interfaces/deepLinks.ts export const deepLinkSchemas = { @@ -59,6 +60,7 @@ type ClaimParams = z.infer<typeof claimSchema>; ``` 2. **Type Generation** + ```typescript // Types are automatically generated from schemas export type DeepLinkParams = { @@ -67,6 +69,7 @@ type ClaimParams = z.infer<typeof claimSchema>; ``` 3. **Runtime Validation** + ```typescript // In DeepLinkHandler const result = deepLinkSchemas.claim.safeParse(params); diff --git a/doc/README-BUILD-GUARD.md b/doc/README-BUILD-GUARD.md new file mode 100644 index 00000000..36f4c21a --- /dev/null +++ b/doc/README-BUILD-GUARD.md @@ -0,0 +1,336 @@ +# Build Architecture Guard - Husky Implementation + +## Overview + +The Build Architecture Guard protects your build system by enforcing +documentation requirements through **Git hooks**. When you modify +build-critical files, the system automatically blocks commits/pushes +until you update `BUILDING.md`. + +## 🎯 **Why Husky-Only?** + +**Advantages:** + +- ✅ **Immediate feedback** - Hooks run before commit/push +- ✅ **Works everywhere** - No server-side CI/CD required +- ✅ **Simple setup** - One tool, one configuration +- ✅ **Fast execution** - No network delays or server queues +- ✅ **Offline support** - Works without internet connection + +**Trade-offs:** + +- ⚠️ **Can be bypassed** - `git commit --no-verify` or `git push --no-verify` +- ⚠️ **Developer discipline** - Relies on team following the rules + +## 🏗️ **Architecture** + +```bash +Developer Workflow: +1. Modify build files (scripts/, vite.config.*, etc.) +2. Try to commit → Husky pre-commit hook runs +3. Guard script checks if BUILDING.md was updated +4. ✅ Commit succeeds if docs updated +5. ❌ Commit blocked if docs missing +``` + +## 🚀 **Quick Start** + +### 1. Install Dependencies + +```bash +npm install +npm run prepare # Sets up Husky hooks +``` + +### 2. Test the System + +```bash +# Modify a build file without updating BUILDING.md +echo "# test" >> scripts/test.sh + +# Try to commit (should be blocked) +git add scripts/test.sh +git commit -m "test: add build script" +# ❌ Hook blocks commit with helpful message +``` + +### 3. Fix and Retry + +```bash +# Update BUILDING.md with your changes +echo "## New Build Script" >> BUILDING.md +echo "Added test.sh for testing purposes" >> BUILDING.md + +# Now commit should succeed +git add BUILDING.md +git commit -m "feat: add test build script with docs" +# ✅ Commit succeeds +``` + +## 🔧 **How It Works** + +### Pre-commit Hook (`.husky/pre-commit`) + +- **When**: Every `git commit` +- **What**: Runs `./scripts/build-arch-guard.sh --staged` +- **Result**: Blocks commit if build files changed without BUILDING.md update + +### Pre-push Hook (`.husky/pre-push`) + +- **When**: Every `git push` +- **What**: Runs `./scripts/build-arch-guard.sh --range` +- **Result**: Blocks push if commits contain undocumented build changes + +### Guard Script (`scripts/build-arch-guard.sh`) + +- **Detects**: Changes to build-sensitive file patterns +- **Validates**: BUILDING.md was updated alongside changes +- **Reports**: Clear error messages with guidance + +## 📁 **Protected File Patterns** + +The guard script monitors these paths for changes: + +```text +Build Configuration: +├── vite.config.* # Vite configuration +├── capacitor.config.ts # Capacitor configuration +├── package.json # Package configuration +├── package-lock.json # Lock files +├── yarn.lock +└── pnpm-lock.yaml + +Build Scripts: +├── scripts/** # All build and automation scripts +├── electron/** # Electron build files +├── android/** # Android build configuration +├── ios/** # iOS build configuration +├── sw_scripts/** # Service worker scripts +└── sw_combine.js # Service worker combination + +Deployment: +├── Dockerfile # Docker configuration +└── docker/** # Docker services +``` + +## 🎭 **Usage Scenarios** + +### Scenario 1: Adding a New Build Script + +```bash +# ❌ This will be blocked +echo '#!/bin/bash' > scripts/new-build.sh +git add scripts/new-build.sh +git commit -m "feat: add new build script" +# Hook blocks: "Build-sensitive files changed but BUILDING.md not updated" + +# ✅ This will succeed +echo '#!/bin/bash' > scripts/new-build.sh +echo '## New Build Script' >> BUILDING.md +echo 'Added new-build.sh for feature X' >> BUILDING.md +git add scripts/new-build.sh BUILDING.md +git commit -m "feat: add new build script with docs" +# ✅ Commit succeeds +``` + +### Scenario 2: Updating Vite Configuration + +```bash +# ❌ This will be blocked +echo 'export default { newOption: true }' >> vite.config.ts +git add vite.config.ts +git commit -m "config: add new vite option" +# Hook blocks: "Build-sensitive files changed but BUILDING.md not updated" + +# ✅ This will succeed +echo 'export default { newOption: true }' >> vite.config.ts +echo '### New Vite Option' >> BUILDING.md +echo 'Added newOption for improved performance' >> BUILDING.md +git add vite.config.ts BUILDING.md +git commit -m "config: add new vite option with docs" +# ✅ Commit succeeds +``` + +## 🚨 **Emergency Bypass** + +**⚠️ Use sparingly and only for emergencies:** + +```bash +# Skip pre-commit hook +git commit -m "emergency: critical fix" --no-verify + +# Skip pre-push hook +git push --no-verify + +# Remember to update BUILDING.md later! +``` + +## 🔍 **Troubleshooting** + +### Hooks Not Running + +```bash +# Reinstall hooks +npm run prepare + +# Check hook files exist and are executable +ls -la .husky/ +chmod +x .husky/* + +# Verify Git hooks path +git config core.hooksPath +# Should show: .husky +``` + +### Guard Script Issues + +```bash +# Test guard script manually +./scripts/build-arch-guard.sh --help + +# Check script permissions +chmod +x scripts/build-arch-guard.sh + +# Test with specific files +./scripts/build-arch-guard.sh --staged +``` + +### False Positives + +```bash +# If guard blocks legitimate changes, check: +# 1. Are you modifying a protected file pattern? +# 2. Did you update BUILDING.md? +# 3. Is BUILDING.md staged for commit? + +# View what the guard sees +git diff --name-only --cached +``` + +## 📋 **Best Practices** + +### For Developers + +1. **Update BUILDING.md first** - Document changes before implementing +2. **Test locally** - Run `./scripts/build-arch-guard.sh --staged` before committing +3. **Use descriptive commits** - Include context about build changes +4. **Don't bypass lightly** - Only use `--no-verify` for true emergencies + +### For Teams + +1. **Document the system** - Ensure everyone understands the guard +2. **Review BUILDING.md updates** - Verify documentation quality +3. **Monitor bypass usage** - Track when hooks are skipped +4. **Regular audits** - Check that BUILDING.md stays current + +### For Maintainers + +1. **Update protected patterns** - Modify `scripts/build-arch-guard.sh` as needed +2. **Monitor effectiveness** - Track how often the guard catches issues +3. **Team training** - Help developers understand the system +4. **Continuous improvement** - Refine patterns and error messages + +## 🚨 **Troubleshooting** + +### Common Issues + +#### mapfile Command Not Found + +**Problem**: Pre-commit hook fails with `mapfile: command not found` + +**Cause**: The `mapfile` command is bash-specific and not available in all shell environments + +**Solution**: The script has been updated to use portable alternatives. If you encounter this issue: + +```bash +# Verify the script is executable +chmod +x scripts/build-arch-guard.sh + +# Test the script directly +./scripts/build-arch-guard.sh --help + +# Check your shell environment +echo $SHELL +bash --version +``` + +**Prevention**: Ensure your development environment uses bash and the script has proper permissions + +### False Positives + +```bash +# If guard blocks legitimate changes, check: +# 1. Are you modifying a protected file pattern? +# 2. Did you update BUILDING.md? +# 3. Is BUILDING.md staged for commit? + +# View what the guard sees +git diff --name-only --cached +``` + +## 🔄 **Customization** + +### Adding New Protected Paths + +Edit `scripts/build-arch-guard.sh`: + +```bash +SENSITIVE=( + # ... existing patterns ... + "new-pattern/**" # Add your new pattern + "*.config.js" # Add file extensions +) +``` + +### Modifying Error Messages + +Edit the guard script to customize: + +- Error message content +- File pattern matching +- Documentation requirements +- Bypass instructions + +### Adding New Validation Rules + +Extend the guard script to check for: + +- Specific file content patterns +- Required documentation sections +- Commit message formats +- Branch naming conventions + +## 📚 **Integration with PR Template** + +The `pull_request_template.md` works with this system by: + +- **Guiding developers** through required documentation +- **Ensuring consistency** across all build changes +- **Providing checklist** for comprehensive updates +- **Supporting L1/L2/L3** change classification + +## 🎯 **Success Metrics** + +Track the effectiveness of your Build Architecture Guard: + +- **Hook execution rate** - How often hooks run successfully +- **Bypass frequency** - How often `--no-verify` is used +- **Documentation quality** - BUILDING.md stays current +- **Build failures** - Fewer issues from undocumented changes +- **Team adoption** - Developers follow the process + +--- + +**Status**: Active protection system +**Architecture**: Client-side Git hooks only +**Dependencies**: Husky, Git, Bash +**Maintainer**: Development team +**Related**: `pull_request_template.md`, `scripts/build-arch-guard.sh` + +## 📝 **Changelog** + +### 2025-08-22 - Shell Compatibility Fix +- **Fixed**: Replaced `mapfile` command with portable alternative for cross-shell compatibility +- **Impact**: Resolves "mapfile: command not found" errors in pre-commit hooks +- **Files**: `scripts/build-arch-guard.sh` +- **Testing**: Script now works across different shell environments diff --git a/doc/README.md b/doc/README.md index 45e98fc4..3e876ca0 100644 --- a/doc/README.md +++ b/doc/README.md @@ -6,7 +6,7 @@ This uses Pandoc and BasicTex (LaTeX) Installed through Homebrew. ### Set Up -```bash +```bash brew install pandoc brew install basictex @@ -54,7 +54,7 @@ sudo tlmgr install sourceserifpro The following guide was adapted to this project except that we install with Brew and have a few more packages. -Guide: https://daniel.feldroy.com/posts/setting-up-latex-on-mac-os-x +Guide: <https://daniel.feldroy.com/posts/setting-up-latex-on-mac-os-x> ### Usage @@ -71,6 +71,7 @@ open usage-guide.pdf ``` Or use this one-liner + ```bash pandoc usage-guide.md -o usage-guide.pdf && open usage-guide.pdf ``` diff --git a/doc/android-asset-validation.md b/doc/android-asset-validation.md new file mode 100644 index 00000000..db77aeb8 --- /dev/null +++ b/doc/android-asset-validation.md @@ -0,0 +1,238 @@ +# Android Asset Validation System + +**Author**: Matthew Raymer +**Date**: 2025-08-22 +**Status**: 🎯 **ACTIVE** - Production Ready + +## Overview + +The Android Asset Validation System automatically detects and fixes missing Android resources before building, preventing common build failures related to missing splash screens and app icons. + +## Problem Solved + +Previously, Android builds would fail with errors like: +``` +error: resource drawable/splash (aka app.timesafari.app:drawable/splash) not found. +error: resource mipmap/ic_launcher (aka app.timesafari.app:mipmap/ic_launcher) not found. +``` + +This happened when: +- Source assets existed but weren't generated into Android resources +- Android resource directories were missing +- Asset generation tools weren't run before building + +## Solution + +### Enhanced Build Script Validation + +The `scripts/build-android.sh` script now includes comprehensive asset validation that: + +1. **Checks Source Assets**: Validates that required source files exist in `resources/` +2. **Checks Android Resources**: Verifies that generated Android resources exist +3. **Auto-Regenerates**: Automatically regenerates missing resources when detected +4. **Verifies Results**: Confirms that regeneration was successful + +### Validation Process + +```bash +# Validates and regenerates if needed +npm run assets:validate:android + +# Full build with validation +npm run build:android:studio +``` + +### What Gets Validated + +#### Source Assets (Required) +- `resources/icon.png` - App icon source +- `resources/splash.png` - Splash screen source +- `resources/splash_dark.png` - Dark mode splash source + +#### Android Resources (Generated) +- `android/app/src/main/res/drawable/splash.png` - Splash screen drawable +- `android/app/src/main/res/mipmap-*/ic_launcher.png` - App icons for all densities +- `android/app/src/main/res/mipmap-*/ic_launcher_round.png` - Round app icons for all densities + +### Density Levels Checked +- `mipmap-mdpi` (1x) +- `mipmap-hdpi` (1.5x) +- `mipmap-xhdpi` (2x) +- `mipmap-xxhdpi` (3x) +- `mipmap-xxxhdpi` (4x) + +## Usage + +### Automatic Validation (Recommended) +The validation runs automatically during all Android builds: + +```bash +# Development build with validation +npm run build:android:studio + +# Production build with validation +npm run build:android:prod + +# Debug build with validation +npm run build:android:debug +``` + +### Manual Validation +Run validation only to check/fix assets: + +```bash +# Validate and regenerate if needed +npm run assets:validate:android + +# Alternative command +./scripts/build-android.sh --assets-only +``` + +### Validation Only (No Regeneration) +Check configuration without fixing: + +```bash +npm run assets:validate +``` + +## Error Handling + +### Missing Source Assets +If source assets are missing, the build fails with clear error messages: + +``` +[ERROR] Missing source assets: +[ERROR] - resources/icon.png +[ERROR] - resources/splash.png +[ERROR] Please ensure all required assets are present in the resources/ directory. +``` + +### Missing Generated Resources +If generated resources are missing, they're automatically regenerated: + +``` +[WARN] Missing Android resources detected: +[WARN] - drawable/splash.png +[WARN] - mipmap-mdpi/ic_launcher.png +[INFO] Regenerating Android assets... +[SUCCESS] Android assets regenerated successfully +``` + +### Generation Failure +If regeneration fails, helpful guidance is provided: + +``` +[ERROR] Failed to generate Android assets +[INFO] You may need to manually create the missing resources: +[INFO] - android/app/src/main/res/drawable/splash.png +[INFO] - android/app/src/main/res/mipmap-mdpi/ic_launcher.png +``` + +## Integration Points + +### Build Script Integration +The validation is integrated into the main build process: + +```bash +# In scripts/build-android.sh +validate_dependencies +validate_android_assets || { + log_error "Android asset validation failed. Please fix the issues above and try again." + exit 9 +} +``` + +### NPM Scripts +New npm scripts for asset management: + +```json +{ + "assets:validate": "npx tsx scripts/assets-validator.ts", + "assets:validate:android": "./scripts/build-android.sh --assets-only", + "assets:clean": "rimraf android/app/src/main/res/mipmap-* ios/App/App/Assets.xcassets/**/AppIcon*.png ios/App/App/Assets.xcassets/**/Splash*.png || true" +} +``` + +## Benefits + +### For Developers +- **No More Build Failures**: Automatic detection and fixing of missing resources +- **Faster Development**: No need to manually run asset generation tools +- **Clear Error Messages**: Helpful guidance when issues occur +- **Consistent Results**: Same validation on all development machines + +### For CI/CD +- **Reliable Builds**: Consistent asset validation across environments +- **Early Detection**: Catches issues before they reach production +- **Automated Fixes**: Self-healing builds when possible + +### For Project Maintenance +- **Reduced Support**: Fewer "build doesn't work" issues +- **Documentation**: Clear requirements for required assets +- **Standardization**: Consistent asset structure across the project + +## Troubleshooting + +### Common Issues + +#### "No assets found in the asset path" +This occurs when the `assets/` directory is empty. The validation system automatically copies source assets and regenerates them. + +#### "Failed to generate Android assets" +Check that: +- Source assets exist in `resources/` +- `@capacitor/assets` is installed +- You have write permissions to the Android directories + +#### "Asset generation completed but some resources are still missing" +This indicates a problem with the asset generation tool. Try: +1. Running `npm install` to ensure dependencies are up to date +2. Manually running `npx @capacitor/assets generate` +3. Checking the asset generation logs for specific errors + +### Manual Recovery +If automatic regeneration fails, you can manually create the missing resources: + +```bash +# Create missing directories +mkdir -p android/app/src/main/res/drawable +mkdir -p android/app/src/main/res/mipmap-{mdpi,hdpi,xhdpi,xxhdpi,xxxhdpi} + +# Copy source assets to assets directory +cp resources/icon.png assets/ +cp resources/splash.png assets/ +cp resources/splash_dark.png assets/ + +# Generate assets manually +npx @capacitor/assets generate + +# Clean up +rm assets/icon.png assets/splash.png assets/splash_dark.png +``` + +## Future Enhancements + +### Planned Improvements +- **iOS Asset Validation**: Extend validation to iOS assets +- **Asset Quality Checks**: Validate image dimensions and formats +- **Performance Optimization**: Cache validation results +- **CI/CD Integration**: Add validation to GitHub Actions + +### Configuration Options +- **Custom Asset Paths**: Support for different asset directory structures +- **Validation Rules**: Configurable validation requirements +- **Skip Options**: Ability to skip validation for specific scenarios + +## References + +- [Capacitor Assets Documentation](https://github.com/ionic-team/capacitor-assets) +- [Android Resource System](https://developer.android.com/guide/topics/resources/providing-resources) +- [Build Script Documentation](./build-android.sh) +- [Asset Configuration](./capacitor-assets.config.json) + +--- + +**Status**: Active validation system +**Priority**: High +**Maintainer**: Development team +**Next Review**: 2025-09-22 diff --git a/doc/architecture-decisions.md b/doc/architecture-decisions.md index 40fa3d3f..11861b8f 100644 --- a/doc/architecture-decisions.md +++ b/doc/architecture-decisions.md @@ -122,4 +122,4 @@ export default class HomeView extends Vue { --- -*This decision was made based on the current codebase architecture and team expertise. The mixin approach provides the best balance of performance, developer experience, and architectural consistency for the TimeSafari application.* \ No newline at end of file +*This decision was made based on the current codebase architecture and team expertise. The mixin approach provides the best balance of performance, developer experience, and architectural consistency for the TimeSafari application.* diff --git a/doc/asset-migration-plan.md b/doc/asset-migration-plan.md new file mode 100644 index 00000000..86c0954e --- /dev/null +++ b/doc/asset-migration-plan.md @@ -0,0 +1,215 @@ +# TimeSafari Asset Configuration Migration Plan + +**Author**: Matthew Raymer +**Date**: 2025-08-14 +**Status**: 🎯 **IMPLEMENTATION** - Ready for Execution + +## Overview + +This document outlines the migration from the current mixed asset management +system to a standardized, single-source asset configuration approach using +`capacitor-assets` as the standard generator. + +## Current State Analysis + +### Asset Sources (Duplicated) + +- **`assets/` directory**: Contains `icon.png`, `splash.png`, `splash_dark.png` +- **`resources/` directory**: Contains identical files in platform-specific subdirectories +- **Result**: Duplicate storage, confusion about source of truth + +### Asset Generation (Manual) + +- **Custom scripts**: `generate-icons.sh`, `generate-ios-assets.sh`, `generate-android-icons.sh` +- **Bypass capacitor-assets**: Manual ImageMagick-based generation +- **Inconsistent outputs**: Different generation methods for each platform + +### Configuration (Scattered) + +- **`capacitor-assets.config.json`**: Basic configuration at root +- **Platform-specific configs**: Mixed in various build scripts +- **No validation**: No schema or consistency checks + +## Target State + +### Single Source of Truth + +- **`resources/` directory**: Capacitor default location for source assets +- **Eliminate duplication**: Remove `assets/` directory after migration +- **Standardized paths**: All tools read from `resources/` + +### Standardized Generation + +- **`capacitor-assets`**: Single tool for all platform asset generation +- **Build-time generation**: Assets generated during build, not committed +- **Deterministic outputs**: Same inputs → same outputs every time + +### Centralized Configuration + +- **`config/assets/`**: All asset-related configuration files +- **Schema validation**: JSON schema for configuration validation +- **CI safeguards**: Automated validation and compliance checks + +## Migration Steps + +### Phase 1: Foundation Setup ✅ + +- [x] Create `config/assets/` directory structure +- [x] Create asset configuration schema (`schema.json`) +- [x] Create enhanced capacitor-assets configuration +- [x] Convert `capacitor.config.json` to `capacitor.config.ts` +- [x] Pin Node.js version (`.nvmrc`, `.node-version`) +- [x] Create dev-time asset configuration generator +- [x] Create asset configuration validator +- [x] Add npm scripts for asset management +- [x] Update `.gitignore` with proper asset exclusions +- [x] Create CI workflow for asset validation + +### Phase 2: Validation & Testing + +- [ ] Run `npm run assets:config` to generate new configuration +- [ ] Run `npm run assets:validate` to verify configuration +- [ ] Test `npm run build:native` workflow +- [ ] Verify CI workflow passes all checks +- [ ] Confirm no platform assets are committed to VCS + +### Phase 3: Cleanup & Removal + +- [ ] Remove `assets/` directory (after validation) +- [ ] Remove manual asset generation scripts +- [ ] Remove asset checking scripts +- [ ] Update documentation references +- [ ] Final validation of clean state + +## Implementation Details + +### File Structure + +``` +resources/ # Image sources ONLY + icon.png + splash.png + splash_dark.png + +config/assets/ # Versioned config & schema + capacitor-assets.config.json + schema.json + +scripts/ + assets-config.js # Dev-time config generator + assets-validator.js # Schema validator +``` + +### Configuration Schema + +The schema enforces: + +- Source files must be in `resources/` directory +- Required fields for icon and splash sections +- Android adaptive icon support (foreground/background/monochrome) +- iOS LaunchScreen preferences +- Target directory validation + +### CI Safeguards + +- **Schema validation**: Configuration must comply with schema +- **Source file validation**: All referenced files must exist +- **Platform asset denial**: Reject commits with generated assets +- **Clean tree enforcement**: Build must not modify committed configs + +## Testing Strategy + +### Local Validation + +```bash +# Generate configuration +npm run assets:config + +# Validate configuration +npm run assets:validate + +# Test build workflow +npm run build:native + +# Clean generated assets +npm run assets:clean +``` + +### CI Validation + +- **Asset validation workflow**: Runs on asset-related changes +- **Schema compliance**: Ensures configuration follows schema +- **Source file existence**: Verifies all referenced files exist +- **Platform asset detection**: Prevents committed generated assets +- **Build tree verification**: Ensures clean tree after build + +## Risk Mitigation + +### Data Loss Prevention + +- **Backup branch**: Create backup before removing `assets/` +- **Validation checks**: Multiple validation steps before removal +- **Gradual migration**: Phase-by-phase approach with rollback capability + +### Build Continuity + +- **Per-platform scripts unchanged**: All existing build orchestration preserved +- **Standard toolchain**: Uses capacitor-assets, not custom scripts +- **Fallback support**: Manual scripts remain until migration complete + +### Configuration Consistency + +- **Schema enforcement**: JSON schema prevents invalid configurations +- **CI validation**: Automated checks catch configuration issues +- **Documentation updates**: Clear guidance for future changes + +## Success Criteria + +### Technical Requirements + +- [ ] Single source of truth in `resources/` directory +- [ ] All platform assets generated via `capacitor-assets` +- [ ] No manual asset generation scripts +- [ ] Configuration validation passes all checks +- [ ] CI workflow enforces asset policies + +### Quality Metrics + +- [ ] Zero duplicate asset sources +- [ ] 100% configuration schema compliance +- [ ] No platform assets committed to VCS +- [ ] Clean build tree after asset generation +- [ ] Deterministic asset outputs + +### User Experience + +- [ ] Clear asset management documentation +- [ ] Simple development commands +- [ ] Consistent asset generation across platforms +- [ ] Reduced confusion about asset sources + +## Next Steps + +1. **Execute Phase 2**: Run validation and testing steps +2. **Verify CI workflow**: Ensure all checks pass +3. **Execute Phase 3**: Remove duplicate assets and scripts +4. **Update documentation**: Finalize README and BUILDING.md +5. **Team training**: Ensure all developers understand new workflow + +## Rollback Plan + +If issues arise during migration: + +1. **Restore backup branch**: `git checkout backup-before-asset-migration` +2. **Revert configuration changes**: Remove new config files +3. **Restore manual scripts**: Re-enable previous asset generation +4. **Investigate issues**: Identify and resolve root causes +5. **Plan revised migration**: Adjust approach based on lessons learned + +--- + +**Status**: Ready for Phase 2 execution +**Priority**: High +**Estimated Effort**: 2-3 hours +**Dependencies**: CI workflow validation +**Stakeholders**: Development team diff --git a/doc/build-modernization-context.md b/doc/build-modernization-context.md index 133c2ad6..4f2ae09e 100644 --- a/doc/build-modernization-context.md +++ b/doc/build-modernization-context.md @@ -3,11 +3,13 @@ **Author:** Matthew Raymer ## Motivation + - Eliminate manual hacks and post-build scripts for Electron builds - Ensure maintainability, reproducibility, and security of build outputs - Unify build, test, and deployment scripts for developer experience and CI/CD ## Key Technical Decisions + - **Vite is the single source of truth for build output** - All Electron build output (main process, preload, renderer HTML/CSS/JS) is managed by `vite.config.electron.mts` - **CSS injection for Electron is handled by a Vite plugin** @@ -21,6 +23,7 @@ - Renderer assets: `dist-electron/www/` (HTML, CSS, JS) ## Security & Maintenance Checklist + - [x] All scripts and configs are committed and documented - [x] No manual file hacks remain - [x] All build output is deterministic and reproducible @@ -28,24 +31,29 @@ - [x] Documentation (`BUILDING.md`) is up to date ## How to Build Electron + 1. Run: + ```bash ./scripts/build-electron.sh ``` + 2. Output will be in `dist-electron/`: - `main.js`, `preload.js` in root - `www/` contains all renderer assets 3. No manual post-processing is required ## Customization + - **Vite config:** All build output and asset handling is controlled in `vite.config.electron.mts` - **CSS/HTML injection:** Use Vite plugins (see `electron-css-injection` in the config) for further customization - **Build scripts:** All orchestration is in `scripts/` and documented in `BUILDING.md` ## For Future Developers + - Always use Vite plugins/config for build output changes - Never manually edit built files or inject assets post-build - Keep documentation and scripts in sync with the build process --- -This file documents the context and rationale for the build modernization and should be included in the repository for onboarding and future reference. \ No newline at end of file +This file documents the context and rationale for the build modernization and should be included in the repository for onboarding and future reference. diff --git a/doc/circular-dependency-analysis.md b/doc/circular-dependency-analysis.md index 0ae322dc..a6530e62 100644 --- a/doc/circular-dependency-analysis.md +++ b/doc/circular-dependency-analysis.md @@ -13,27 +13,31 @@ The codebase currently has **no active circular dependencies** that are causing ### 🔍 **Resolved Dependency Patterns** #### 1. **Logger → PlatformServiceFactory → Logger** (RESOLVED) + - **Status**: ✅ **RESOLVED** - **Previous Issue**: Logger imported `logToDb` from databaseUtil, which imported logger - **Solution**: Logger now uses direct database access via PlatformServiceFactory - **Implementation**: Self-contained `logToDatabase()` function in logger.ts #### 2. **PlatformServiceMixin → databaseUtil → logger → PlatformServiceMixin** (RESOLVED) + - **Status**: ✅ **RESOLVED** - **Previous Issue**: PlatformServiceMixin imported `memoryLogs` from databaseUtil - **Solution**: Created self-contained `_memoryLogs` array in PlatformServiceMixin - **Implementation**: Self-contained memory logs implementation #### 3. **databaseUtil → logger → PlatformServiceFactory → databaseUtil** (RESOLVED) + - **Status**: ✅ **RESOLVED** - **Previous Issue**: databaseUtil imported logger, which could create loops - **Solution**: Logger is now self-contained and doesn't import from databaseUtil #### 4. **Utility Files → databaseUtil → PlatformServiceMixin** (RESOLVED) + - **Status**: ✅ **RESOLVED** - **Previous Issue**: `src/libs/util.ts` and `src/services/deepLinks.ts` imported from databaseUtil - **Solution**: Replaced with self-contained implementations and PlatformServiceFactory usage -- **Implementation**: +- **Implementation**: - Self-contained `parseJsonField()` and `mapQueryResultToValues()` functions - Direct PlatformServiceFactory usage for database operations - Console logging instead of databaseUtil logging functions @@ -43,18 +47,21 @@ The codebase currently has **no active circular dependencies** that are causing ### ✅ **All Critical Dependencies Resolved** #### PlatformServiceMixin Independence + - **Status**: ✅ **COMPLETE** - **Achievement**: PlatformServiceMixin has no external dependencies on databaseUtil - **Implementation**: Self-contained memory logs and utility functions - **Impact**: Enables complete migration of databaseUtil functions to PlatformServiceMixin #### Logger Independence + - **Status**: ✅ **COMPLETE** - **Achievement**: Logger is completely self-contained - **Implementation**: Direct database access via PlatformServiceFactory - **Impact**: Eliminates all circular dependency risks #### Utility Files Independence + - **Status**: ✅ **COMPLETE** - **Achievement**: All utility files no longer depend on databaseUtil - **Implementation**: Self-contained functions and direct platform service access @@ -63,6 +70,7 @@ The codebase currently has **no active circular dependencies** that are causing ### 🎯 **Migration Readiness Status** #### Files Ready for Migration (52 files) + 1. **Components** (15 files): - `PhotoDialog.vue` - `FeedFilters.vue` @@ -98,6 +106,7 @@ The codebase currently has **no active circular dependencies** that are causing ### 🟢 **Healthy Dependencies** #### Logger Usage (80+ files) + - **Status**: ✅ **HEALTHY** - **Pattern**: All files import logger from `@/utils/logger` - **Impact**: No circular dependencies, logger is self-contained @@ -106,21 +115,25 @@ The codebase currently has **no active circular dependencies** that are causing ## Resolution Strategy - COMPLETED ### ✅ **Phase 1: Complete PlatformServiceMixin Independence (COMPLETE)** + 1. **Removed memoryLogs import** from PlatformServiceMixin ✅ 2. **Created self-contained memoryLogs** implementation ✅ 3. **Added missing utility methods** to PlatformServiceMixin ✅ ### ✅ **Phase 2: Utility Files Migration (COMPLETE)** + 1. **Migrated deepLinks.ts** - Replaced databaseUtil logging with console logging ✅ 2. **Migrated util.ts** - Replaced databaseUtil functions with self-contained implementations ✅ 3. **Updated all PlatformServiceFactory calls** to use async pattern ✅ ### 🎯 **Phase 3: File-by-File Migration (READY TO START)** + 1. **High-usage files first** (views, core components) 2. **Replace databaseUtil imports** with PlatformServiceMixin 3. **Update function calls** to use mixin methods ### 🎯 **Phase 4: Cleanup (FUTURE)** + 1. **Remove unused databaseUtil functions** 2. **Update TypeScript interfaces** 3. **Remove databaseUtil imports** from all files @@ -128,6 +141,7 @@ The codebase currently has **no active circular dependencies** that are causing ## Current Status Summary ### ✅ **Resolved Issues** + 1. **Logger circular dependency** - Fixed with self-contained implementation 2. **PlatformServiceMixin circular dependency** - Fixed with self-contained memoryLogs 3. **Utility files circular dependency** - Fixed with self-contained implementations @@ -135,6 +149,7 @@ The codebase currently has **no active circular dependencies** that are causing 5. **Runtime stability** - No circular dependency crashes ### 🎯 **Ready for Next Phase** + 1. **52 files** ready for databaseUtil migration 2. **PlatformServiceMixin** fully independent and functional 3. **Clear migration path** - Well-defined targets and strategy @@ -142,6 +157,7 @@ The codebase currently has **no active circular dependencies** that are causing ## Benefits of Current State ### ✅ **Achieved** + 1. **No runtime circular dependencies** - Application runs without crashes 2. **Self-contained logger** - No more logger/databaseUtil loops 3. **PlatformServiceMixin ready** - All methods implemented and independent @@ -149,6 +165,7 @@ The codebase currently has **no active circular dependencies** that are causing 5. **Clear migration path** - Well-defined targets and strategy ### 🎯 **Expected After Migration** + 1. **Complete databaseUtil migration** - Single source of truth 2. **Eliminated circular dependencies** - Clean architecture 3. **Improved performance** - Caching and optimization @@ -160,4 +177,4 @@ The codebase currently has **no active circular dependencies** that are causing **Created**: 2025-07-05 **Status**: ✅ **COMPLETE - All Circular Dependencies Resolved** **Last Updated**: 2025-01-06 -**Note**: PlatformServiceMixin circular dependency completely resolved. Ready for Phase 2: File-by-File Migration \ No newline at end of file +**Note**: PlatformServiceMixin circular dependency completely resolved. Ready for Phase 2: File-by-File Migration diff --git a/doc/component-communication-guide.md b/doc/component-communication-guide.md index 1fad5968..797e5740 100644 --- a/doc/component-communication-guide.md +++ b/doc/component-communication-guide.md @@ -93,6 +93,7 @@ export default class FormComponent extends Vue { When generating component templates, follow these patterns: #### Function Props Template + ```vue <template> <div class="component-name"> @@ -124,6 +125,7 @@ export default class ComponentName extends Vue { ``` #### $emit Template (for DOM events) + ```vue <template> <div class="component-name"> @@ -155,12 +157,14 @@ export default class ComponentName extends Vue { ### Code Generation Rules #### 1. Function Props for Business Logic + - **Data operations**: Save, delete, update, validate - **Navigation**: Route changes, modal opening/closing - **State management**: Store actions, state updates - **API calls**: Data fetching, form submissions #### 2. $emit for User Interactions + - **Click events**: Button clicks, link navigation - **Form events**: Input changes, form submissions - **Lifecycle events**: Component mounting, unmounting @@ -169,6 +173,7 @@ export default class ComponentName extends Vue { #### 3. Naming Conventions **Function Props:** + ```typescript // Action-oriented names onSave: (data: SaveData) => Promise<void> @@ -179,6 +184,7 @@ onNavigate: (route: string) => void ``` **$emit Events:** + ```typescript // Event-oriented names @click: (event: MouseEvent) => void @@ -191,6 +197,7 @@ onNavigate: (route: string) => void ### TypeScript Integration #### Function Prop Types + ```typescript // Define reusable function types interface SaveHandler { @@ -207,6 +214,7 @@ interface ValidationHandler { ``` #### Event Types + ```typescript // Define event payload types interface ClickEvent { @@ -226,6 +234,7 @@ handleClick(): ClickEvent { ## Testing Guidelines ### Function Props Testing + ```typescript // Easy to mock and test const mockOnSave = jest.fn(); @@ -240,6 +249,7 @@ expect(mockOnSave).toHaveBeenCalledWith(expectedData); ``` ### $emit Testing + ```typescript // Requires event simulation const wrapper = mount(MyComponent); @@ -260,6 +270,7 @@ expect(wrapper.emitted('click')).toBeTruthy(); ### Example Migration **Before ($emit):** + ```typescript @Emit("save") handleSave() { @@ -268,6 +279,7 @@ handleSave() { ``` **After (Function Props):** + ```typescript @Prop({ required: true }) onSave!: (data: FormData) => void; @@ -288,6 +300,7 @@ handleSave() { ## Code Generation Templates ### Component Generator Input + ```typescript interface ComponentSpec { name: string; @@ -306,9 +319,10 @@ interface ComponentSpec { ``` ### Generated Output + ```typescript // Generator should automatically choose function props vs $emit // based on the nature of the interaction (business logic vs DOM event) ``` -This guide ensures consistent, maintainable component communication patterns across the application. \ No newline at end of file +This guide ensures consistent, maintainable component communication patterns across the application. diff --git a/doc/cors-disabled-for-universal-images.md b/doc/cors-disabled-for-universal-images.md index 6eb73a2c..f688b39f 100644 --- a/doc/cors-disabled-for-universal-images.md +++ b/doc/cors-disabled-for-universal-images.md @@ -7,10 +7,12 @@ CORS headers have been **disabled** to support Time Safari's core mission: enabl ## What Changed ### ❌ Removed CORS Headers + - `Cross-Origin-Opener-Policy: same-origin` - `Cross-Origin-Embedder-Policy: require-corp` ### ✅ Results + - Images from **any domain** now work in development and production - No proxy configuration needed - No whitelist of supported image hosts @@ -19,11 +21,13 @@ CORS headers have been **disabled** to support Time Safari's core mission: enabl ## Technical Tradeoffs ### 🔻 Lost: SharedArrayBuffer Performance + - **Before**: Fast SQLite operations via SharedArrayBuffer - **After**: Slightly slower IndexedDB fallback mode - **Impact**: Minimal for typical usage - absurd-sql automatically falls back ### 🔺 Gained: Universal Image Support + - **Before**: Only specific domains worked (TimeSafari, Flickr, Imgur, etc.) - **After**: Any image URL works immediately - **Impact**: Massive improvement for user experience @@ -31,6 +35,7 @@ CORS headers have been **disabled** to support Time Safari's core mission: enabl ## Architecture Impact ### Database Operations + ```typescript // absurd-sql automatically detects SharedArrayBuffer availability if (typeof SharedArrayBuffer === "undefined") { @@ -43,6 +48,7 @@ if (typeof SharedArrayBuffer === "undefined") { ``` ### Image Loading + ```typescript // All images load directly now export function transformImageUrlForCors(imageUrl: string): string { @@ -53,11 +59,13 @@ export function transformImageUrlForCors(imageUrl: string): string { ## Why This Was The Right Choice ### Time Safari's Use Case + - **Community platform** where users share content from anywhere - **User-generated content** includes images from arbitrary websites - **Flexibility** is more important than marginal performance gains ### Alternative Would Require + - Pre-configuring proxies for every possible image hosting service - Constantly updating proxy list as users find new sources - Poor user experience when images fail to load @@ -66,11 +74,13 @@ export function transformImageUrlForCors(imageUrl: string): string { ## Performance Comparison ### Database Operations + - **SharedArrayBuffer**: ~2x faster for large operations - **IndexedDB**: Still very fast for typical Time Safari usage - **Real Impact**: Negligible for typical user operations ### Image Loading + - **With CORS**: Many images failed to load in development - **Without CORS**: All images load immediately - **Real Impact**: Massive improvement in user experience @@ -87,11 +97,13 @@ export function transformImageUrlForCors(imageUrl: string): string { ## Migration Notes ### For Developers + - No code changes needed - `transformImageUrlForCors()` still exists but returns original URL - All existing image references work without modification ### For Users + - Images from any website now work immediately - No more "image failed to load" issues in development - Consistent behavior between development and production @@ -99,12 +111,14 @@ export function transformImageUrlForCors(imageUrl: string): string { ## Future Considerations ### If Performance Becomes Critical + 1. **Selective CORS**: Enable only for specific operations 2. **Service Worker**: Handle image proxying at service worker level 3. **Build-time Processing**: Pre-process images during build 4. **User Education**: Guide users toward optimized image hosting ### Monitoring + - Track database operation performance - Monitor for any user-reported slowness - Consider re-enabling SharedArrayBuffer if usage patterns change @@ -113,4 +127,4 @@ export function transformImageUrlForCors(imageUrl: string): string { This change prioritizes **user experience** and **community functionality** over marginal performance gains. The database still works efficiently via IndexedDB, while images now work universally without configuration. -For a community platform like Time Safari, the ability to share images from any domain is fundamental to the user experience and mission. \ No newline at end of file +For a community platform like Time Safari, the ability to share images from any domain is fundamental to the user experience and mission. diff --git a/doc/cors-image-loading-solution.md b/doc/cors-image-loading-solution.md index 0ae689ee..c0df26ba 100644 --- a/doc/cors-image-loading-solution.md +++ b/doc/cors-image-loading-solution.md @@ -7,6 +7,7 @@ This document describes the implementation of a comprehensive image loading solu ## Problem Statement When using SharedArrayBuffer (required for absurd-sql), browsers enforce a cross-origin isolated environment with these headers: + - `Cross-Origin-Opener-Policy: same-origin` - `Cross-Origin-Embedder-Policy: require-corp` @@ -19,6 +20,7 @@ This isolation prevents loading external resources (including images) unless the The solution uses a multi-tier approach to handle images from various sources: #### Tier 1: Specific Domain Proxies (Development Only) + - **TimeSafari Images**: `/image-proxy/` → `https://image.timesafari.app/` - **Flickr Images**: `/flickr-proxy/` → `https://live.staticflickr.com/` - **Imgur Images**: `/imgur-proxy/` → `https://i.imgur.com/` @@ -26,14 +28,17 @@ The solution uses a multi-tier approach to handle images from various sources: - **Unsplash**: `/unsplash-proxy/` → `https://images.unsplash.com/` #### Tier 2: Universal CORS Proxy (Development Only) + - **Any External Domain**: Uses `https://api.allorigins.win/raw?url=` for arbitrary domains #### Tier 3: Direct Loading (Production) + - **Production Mode**: All images load directly without proxying ### 2. Smart URL Transformation The `transformImageUrlForCors` function automatically: + - Detects the image source domain - Routes through appropriate proxy in development - Preserves original URLs in production @@ -44,6 +49,7 @@ The `transformImageUrlForCors` function automatically: ### Configuration Files #### `vite.config.common.mts` + ```typescript server: { headers: { @@ -63,6 +69,7 @@ server: { ``` #### `src/libs/util.ts` + ```typescript export function transformImageUrlForCors(imageUrl: string): string { // Development mode: Transform URLs to use proxies @@ -93,21 +100,25 @@ const imageUrl = transformImageUrlForCors(originalImageUrl); ## Benefits ### ✅ SharedArrayBuffer Support + - Maintains cross-origin isolation required for SharedArrayBuffer - Enables fast SQLite database operations via absurd-sql - Provides better performance than IndexedDB fallback ### ✅ Universal Image Support + - Handles images from any domain - No need to pre-configure every possible image source - Graceful fallback for unknown domains ### ✅ Development/Production Flexibility + - Proxy system only active in development - Production uses direct URLs for maximum performance - No proxy server required in production ### ✅ Automatic Detection + - Smart URL transformation based on domain patterns - Preserves relative URLs and data URLs - Handles edge cases gracefully @@ -115,6 +126,7 @@ const imageUrl = transformImageUrlForCors(originalImageUrl); ## Testing ### Automated Testing + Run the test suite to verify URL transformation: ```typescript @@ -125,6 +137,7 @@ testCorsImageTransformation(); ``` ### Visual Testing + Create test image elements to verify loading: ```typescript @@ -135,6 +148,7 @@ createTestImageElements(); ``` ### Manual Testing + 1. Start development server: `npm run dev` 2. Open browser console to see transformation logs 3. Check Network tab for proxy requests @@ -143,16 +157,19 @@ createTestImageElements(); ## Security Considerations ### Development Environment + - CORS proxies are only used in development - External proxy services (allorigins.win) are used for testing - No sensitive data is exposed through proxies ### Production Environment + - All images load directly without proxying - No dependency on external proxy services - Original security model maintained ### Privacy + - Image URLs are not logged or stored by proxy services - Proxy requests are only made during development - No tracking or analytics in proxy chain @@ -160,11 +177,13 @@ createTestImageElements(); ## Performance Impact ### Development + - Slight latency from proxy requests - Additional network hops for external domains - More verbose logging for debugging ### Production + - No performance impact - Direct image loading as before - No proxy overhead @@ -174,17 +193,20 @@ createTestImageElements(); ### Common Issues #### Images Not Loading in Development + 1. Check console for proxy errors 2. Verify CORS headers are set 3. Test with different image URLs 4. Check network connectivity to proxy services #### SharedArrayBuffer Not Available + 1. Verify CORS headers are set in server configuration 2. Check that site is served over HTTPS (or localhost) 3. Ensure browser supports SharedArrayBuffer #### Proxy Service Unavailable + 1. Check if allorigins.win is accessible 2. Consider using alternative CORS proxy services 3. Temporarily disable CORS headers for testing @@ -207,12 +229,14 @@ testCorsImageTransformation(); ## Migration Guide ### From Previous Implementation + 1. CORS headers are now required for SharedArrayBuffer 2. Image URLs automatically transformed in development 3. No changes needed to existing image loading code 4. Test thoroughly in both development and production ### Adding New Image Sources + 1. Add specific proxy for frequently used domains 2. Update `transformImageUrlForCors` function 3. Add CORS headers to proxy configuration @@ -221,6 +245,7 @@ testCorsImageTransformation(); ## Future Enhancements ### Possible Improvements + 1. **Local Proxy Server**: Run dedicated proxy server for development 2. **Caching**: Cache proxy responses for better performance 3. **Fallback Chain**: Multiple proxy services for reliability @@ -228,6 +253,7 @@ testCorsImageTransformation(); 5. **Analytics**: Track image loading success/failure rates ### Alternative Approaches + 1. **Service Worker**: Intercept image requests at service worker level 2. **Build-time Processing**: Pre-process images during build 3. **CDN Integration**: Use CDN with proper CORS headers @@ -237,4 +263,4 @@ testCorsImageTransformation(); This solution provides a robust, scalable approach to image loading in a cross-origin isolated environment while maintaining the benefits of SharedArrayBuffer support. The multi-tier proxy system ensures compatibility with any image source while optimizing for performance and security. -For questions or issues, refer to the troubleshooting section or consult the development team. \ No newline at end of file +For questions or issues, refer to the troubleshooting section or consult the development team. diff --git a/doc/database-migration-guide.md b/doc/database-migration-guide.md index 00103452..f202f5ad 100644 --- a/doc/database-migration-guide.md +++ b/doc/database-migration-guide.md @@ -294,6 +294,7 @@ const result = await this.$db("SELECT * FROM contacts WHERE did = ?", [accountDi ``` This provides: + - **Caching**: Automatic caching for performance - **Error Handling**: Consistent error handling - **Type Safety**: Enhanced TypeScript integration diff --git a/doc/debug-hook-guide.md b/doc/debug-hook-guide.md new file mode 100644 index 00000000..fef2e269 --- /dev/null +++ b/doc/debug-hook-guide.md @@ -0,0 +1,187 @@ +# TimeSafari Debug Hook Guide + +**Complete Guide for Team Members** + +**Date**: 2025-01-27 +**Author**: Matthew Raymer +**Status**: ✅ **ACTIVE** - Ready for production use + +## 🎯 Overview + +A pre-commit hook that automatically detects and prevents debug code from reaching protected branches (master, main, production, release, stable). This ensures production code remains clean while allowing free development on feature branches. + +## 🚀 Quick Installation + +**From within the TimeSafari repository:** + +```bash +./scripts/install-debug-hook.sh +``` + +This automatically installs, updates, and verifies the hook in your current +repository. **Note**: Hooks are not automatically installed - you must run this +script deliberately to enable debug code checking. + +## 🔧 Manual Installation + +**Copy files manually:** + +```bash +cp scripts/git-hooks/pre-commit /path/to/your/repo/.git/hooks/ +cp scripts/git-hooks/debug-checker.config /path/to/your/repo/.git/hooks/ +chmod +x /path/to/your/repo/.git/hooks/pre-commit +``` + +## 📋 What Gets Installed + +- **`pre-commit`** - Main hook script (executable) +- **`debug-checker.config`** - Configuration file +- **`README.md`** - Documentation and troubleshooting + +**Note**: Hooks are stored in `scripts/git-hooks/` and must be deliberately +installed by each developer. They are not automatically active. + +## 🎯 How It Works + +1. **Deliberate Installation**: Hooks must be explicitly installed by each + developer +2. **Branch Detection**: Only runs on protected branches +3. **File Filtering**: Automatically skips tests, scripts, and documentation +4. **Pattern Matching**: Detects debug code using regex patterns +5. **Commit Prevention**: Blocks commits containing debug code + +## 🔒 Installation Philosophy + +**Why deliberate installation?** + +- **Developer choice**: Each developer decides whether to use the hook +- **No forced behavior**: Hooks don't interfere with existing workflows +- **Local control**: Hooks are installed locally, not globally +- **Easy removal**: Can be uninstalled at any time +- **Team flexibility**: Some developers may prefer different tools + +## 🌿 Branch Behavior + +- **Protected branches** (master, main, production, release, stable): Hook runs automatically +- **Feature branches**: Hook is skipped, allowing free development with debug code + +## 🔍 Debug Patterns Detected + +- **Console statements**: `console.log`, `console.debug`, `console.error` +- **Template debug**: `Debug:`, `debug:` in Vue templates +- **Debug constants**: `DEBUG_`, `debug_` variables +- **HTML debug**: `<!-- debug` comments +- **Debug attributes**: `debug="true"` attributes +- **Vue debug**: `v-if="debug"`, `v-show="debug"` +- **Debug TODOs**: `TODO debug`, `FIXME debug` + +## 📁 Files Automatically Skipped + +- Test files: `*.test.js`, `*.spec.ts`, `*.test.vue` +- Scripts: `scripts/` directory +- Test directories: `test-*` directories +- Documentation: `docs/`, `*.md`, `*.txt` +- Config files: `*.json`, `*.yml`, `*.yaml` +- IDE files: `.cursor/` directory + +## ✅ Verification + +**After installation, verify it's working:** + +```bash +# Check if files exist +ls -la .git/hooks/pre-commit +ls -la .git/hooks/debug-checker.config + +# Test the hook manually +.git/hooks/pre-commit + +# Test with actual commit +echo "console.log('test')" > test.vue +git add test.vue +git commit -m "test" # Should be blocked +``` + +## 📊 Example Output + +``` +❌ Debug code detected in staged files! + Branch: master + Files checked: 1 + Errors found: 3 + + 🚨 AccountViewView.vue: Found debug pattern 'console\.' + 🚨 AccountViewView.vue: Found debug pattern 'Debug:' + 🚨 AccountViewView.vue: Found debug pattern 'DEBUG_' + +💡 Please remove debug code before committing to master +``` + +## ⚙️ Configuration + +Edit `.git/hooks/debug-checker.config` to customize: + +- **Protected branches**: Add/remove branches as needed +- **Debug patterns**: Customize what gets detected +- **Skip patterns**: Adjust file filtering rules + +## 🚨 Emergency Bypass + +If you absolutely need to commit debug code to a protected branch: + +```bash +git commit --no-verify -m "emergency: debug code needed" +``` + +⚠️ **Warning**: This bypasses all pre-commit hooks. Use sparingly. + +## 🔄 Updates + +When the hook is updated in the main repository: + +```bash +./scripts/install-debug-hook.sh +``` + +## 🚨 Troubleshooting + +| Issue | Solution | +|-------|----------| +| Hook not running | Check if on protected branch, verify permissions | +| Permission denied | Run `chmod +x .git/hooks/pre-commit` | +| Files not found | Ensure you're copying from TimeSafari repo | +| False positives | Edit `debug-checker.config` to customize patterns | + +## 🧪 Testing + +A test script is available at `scripts/test-debug-hook.sh` to verify the hook works correctly. + +## 💡 Best Practices + +1. **Use feature branches** for development with debug code +2. **Use proper logging** instead of console statements (`logger.info`, `logger.debug`) +3. **Test thoroughly** before merging to protected branches +4. **Review commits** to ensure no debug code slips through +5. **Keep hooks updated** across all repositories + +## 📚 Additional Resources + +- **Hook documentation**: `scripts/git-hooks/README.md` +- **Configuration**: `scripts/git-hooks/debug-checker.config` +- **Test script**: `scripts/test-debug-hook.sh` +- **Installation script**: `scripts/install-debug-hook.sh` + +## 🎯 Team Workflow + +**Recommended setup:** + +1. **Repository setup**: Include hook files in `.githooks/` directory +2. **Team onboarding**: Run installation script in each repo +3. **Updates**: Re-run installation script when hooks are updated +4. **Documentation**: Keep this guide updated + +--- + +**Status**: Active and enforced +**Last Updated**: 2025-01-27 +**Maintainer**: Matthew Raymer diff --git a/doc/electron-cleanup-summary.md b/doc/electron-cleanup-summary.md index 94b7ef5b..c904ef42 100644 --- a/doc/electron-cleanup-summary.md +++ b/doc/electron-cleanup-summary.md @@ -7,18 +7,22 @@ This document summarizes the comprehensive cleanup and improvements made to the ## Key Issues Resolved ### 1. Platform Detection Problems + - **Before**: `PlatformServiceFactory` only supported "capacitor" and "web" platforms - **After**: Added proper "electron" platform support with dedicated `ElectronPlatformService` ### 2. Build Configuration Confusion + - **Before**: Electron builds used `VITE_PLATFORM=capacitor`, causing confusion - **After**: Electron builds now properly use `VITE_PLATFORM=electron` ### 3. Missing Platform Service Methods + - **Before**: Platform services lacked proper `isElectron()`, `isCapacitor()`, `isWeb()` methods - **After**: All platform services implement complete interface with proper detection ### 4. Inconsistent Build Scripts + - **Before**: Mixed platform settings in build scripts - **After**: Clean, consistent electron-specific build process @@ -215,11 +219,13 @@ if (capabilities.hasFileDownload) { ## File Structure Changes ### New Files + - `vite.config.electron.mts` - Electron-specific Vite configuration - `src/main.electron.ts` - Electron main entry point - `doc/electron-cleanup-summary.md` - This documentation ### Modified Files + - `src/services/PlatformServiceFactory.ts` - Added electron platform support - `src/services/PlatformService.ts` - Added platform detection methods - `src/services/platforms/CapacitorPlatformService.ts` - Added missing interface methods @@ -301,4 +307,4 @@ For developers working with the previous implementation: - [ ] Implement desktop-specific UI components - [ ] Add Electron auto-updater integration - [ ] Create platform-specific testing utilities -- [ ] Add desktop notification system integration \ No newline at end of file +- [ ] Add desktop notification system integration diff --git a/doc/electron-console-cleanup.md b/doc/electron-console-cleanup.md index a979f972..ef89667c 100644 --- a/doc/electron-console-cleanup.md +++ b/doc/electron-console-cleanup.md @@ -7,18 +7,22 @@ This document summarizes the comprehensive changes made to reduce excessive cons ## Issues Addressed ### 1. Excessive Database Logging (Major Issue - 90% Reduction) + **Problem:** Every database operation was logging detailed parameter information, creating hundreds of lines of console output. **Solution:** Modified `src/services/platforms/CapacitorPlatformService.ts`: + - Changed `logger.warn` to `logger.debug` for routine SQL operations -- Reduced migration logging verbosity +- Reduced migration logging verbosity - Made database integrity checks use debug-level logging - Kept error and completion messages at appropriate log levels ### 2. Enhanced Logger Configuration + **Problem:** No platform-specific logging controls, causing noise in Electron. **Solution:** Updated `src/utils/logger.ts`: + - Added platform detection for Electron vs Web - Suppressed debug and verbose logs for Electron - Filtered out routine database operations from database logging @@ -26,28 +30,35 @@ This document summarizes the comprehensive changes made to reduce excessive cons - Added intelligent filtering for CapacitorPlatformService messages ### 3. API Configuration Issues (Major Fix) + **Problem:** Electron was trying to use local development endpoints (localhost:3000) from saved user settings, which don't exist in desktop environment, causing: + - 400 status errors from missing local development servers - JSON parsing errors (HTML error pages instead of JSON responses) -**Solution:** +**Solution:** + - Updated `src/constants/app.ts` to provide Electron-specific API endpoints - **Critical Fix:** Modified `src/db/databaseUtil.ts` in `retrieveSettingsForActiveAccount()` to force Electron to use production API endpoints regardless of saved user settings - This ensures Electron never uses localhost development servers that users might have saved ### 4. SharedArrayBuffer Logging Noise + **Problem:** Web-specific SharedArrayBuffer detection was running in Electron, creating unnecessary debug output. **Solution:** Modified `src/main.web.ts`: + - Made SharedArrayBuffer logging conditional on web platform only - Converted console.log statements to logger.debug - Only show in development mode for web platform - Reduced platform detection noise ### 5. Missing Source Maps Warnings + **Problem:** Electron DevTools was complaining about missing source maps for external dependencies. **Solution:** Updated `vite.config.electron.mts`: + - Disabled source maps for Electron builds (`sourcemap: false`) - Added build configuration to suppress external dependency warnings - Prevents DevTools from looking for non-existent source map files @@ -87,14 +98,16 @@ This document summarizes the comprehensive changes made to reduce excessive cons ## Impact -### Before Cleanup: +### Before Cleanup + - 500+ lines of console output per minute - Detailed SQL parameter logging for every operation - API connection errors every few seconds (400 status, JSON parsing errors) - SharedArrayBuffer warnings on every startup - DevTools source map warnings -### After Cleanup: +### After Cleanup + - **~95% reduction** in console output - Only errors and important status messages visible - **No API connection errors** - Electron uses proper production endpoints @@ -106,6 +119,7 @@ This document summarizes the comprehensive changes made to reduce excessive cons ## Technical Details ### API Configuration Fix + The most critical fix was in `src/db/databaseUtil.ts` where we added: ```typescript @@ -122,6 +136,7 @@ if (process.env.VITE_PLATFORM === "electron") { This ensures that even if users have localhost development endpoints saved in their settings, Electron will override them with production endpoints. ### Logger Enhancement + Enhanced the logger with platform-specific behavior: ```typescript @@ -135,6 +150,7 @@ if (!isElectron || !message.includes("[CapacitorPlatformService]")) { ## Testing The changes were tested with: + - `npm run lint-fix` - 0 errors, warnings only (pre-existing) - Electron development environment - Web platform (unchanged functionality) @@ -150,6 +166,7 @@ The changes were tested with: ## Backward Compatibility All changes maintain backward compatibility: + - Web platform logging unchanged - Capacitor platform logging unchanged - Error handling preserved @@ -185,4 +202,4 @@ Tests: lint passes, Web/Capacitor functionality preserved 1. **Test the fixes** - Run `npm run electron:dev` to verify console noise is eliminated 2. **Monitor for remaining issues** - Check for any other console noise sources 3. **Performance monitoring** - Verify the reduced logging doesn't impact functionality -4. **Documentation updates** - Update any development guides that reference the old logging behavior \ No newline at end of file +4. **Documentation updates** - Update any development guides that reference the old logging behavior diff --git a/doc/error-diagnostics-log.md b/doc/error-diagnostics-log.md index 0a54ba1b..78763433 100644 --- a/doc/error-diagnostics-log.md +++ b/doc/error-diagnostics-log.md @@ -5,9 +5,10 @@ This file tracks console errors observed during development for future investiga ## 2025-07-07 08:56 UTC - ProjectsView.vue Migration Session ### Migration Context + - **Current Work**: Completed ProjectsView.vue Triple Migration Pattern - **Migration Status**: 21 complete, 4 appropriately incomplete components -- **Recent Changes**: +- **Recent Changes**: - ProjectsView.vue: databaseUtil → PlatformServiceMixin - Added notification constants and literal string extraction - Template logic streamlining with computed properties @@ -15,42 +16,50 @@ This file tracks console errors observed during development for future investiga ### Observed Errors #### 1. HomeView.vue API Rate Limit Errors + ``` GET https://api.endorser.ch/api/report/rateLimits 400 (Bad Request) Source: endorserServer.ts:1494, HomeView.vue:593, HomeView.vue:742 ``` -**Analysis**: +**Analysis**: + - API server returning 400 for rate limit checks - Occurs during identity initialization and registration status checks - **Migration Impact**: None - HomeView.vue was migrated and tested earlier - **Likely Cause**: Server-side authentication or API configuration issue **Action Items**: + - [ ] Check endorser.ch API documentation for rate limit endpoint changes - [ ] Verify authentication headers being sent correctly - [ ] Consider fallback handling for rate limit API failures #### 2. ProjectViewView.vue Project Not Found Error + ``` GET https://api.endorser.ch/api/claim/byHandle/...01JY2Q5D90E8P267ABB963S71D 404 (Not Found) Source: ProjectViewView.vue:830 loadProject() method ``` **Analysis**: + - Attempting to load project ID: `01JY2Q5D90E8P267ABB963S71D` - **Migration Impact**: None - error handling working correctly - **Likely Cause**: User navigated to non-existent project or stale link **Action Items**: + - [ ] Consider adding better user messaging for missing projects - [ ] Investigate if project IDs are being generated/stored correctly - [ ] Add breadcrumb or "return to projects" option on 404s #### 3. Axios Request Stack Traces + Multiple stack traces showing Vue router navigation and component mounting cycles. **Analysis**: + - Normal Vue.js lifecycle and routing behavior - No obvious memory leaks or infinite loops - **Migration Impact**: None - expected framework behavior @@ -58,26 +67,30 @@ Multiple stack traces showing Vue router navigation and component mounting cycle ### System Health Indicators #### ✅ Working Correctly + - Database migrations: `Migration process complete! Summary: 0 applied, 2 skipped` - Platform service factory initialization: `Creating singleton instance for platform: development` - SQL worker loading: `Worker loaded, ready to receive messages` - Database connection: `Opened!` #### 🔄 For Investigation + - API authentication/authorization with endorser.ch - Project ID validation and error handling - Rate limiting strategy ### Migration Validation + - **ProjectsView.vue**: Appropriately incomplete (3 helpers + 1 complex modal) - **Error Handling**: Migrated components showing proper error handling - **No Migration-Related Errors**: All errors appear to be infrastructure/data issues ### Next Steps + 1. Continue migration slog with next component 2. Monitor these same error patterns in future sessions 3. Address API/server issues in separate debugging session --- *Log Entry by: Migration Assistant* -*Session: ProjectsView.vue Triple Migration Pattern* \ No newline at end of file +*Session: ProjectsView.vue Triple Migration Pattern* diff --git a/doc/image-hosting-guide.md b/doc/image-hosting-guide.md index d94ab450..1a8d1e04 100644 --- a/doc/image-hosting-guide.md +++ b/doc/image-hosting-guide.md @@ -25,6 +25,7 @@ ## Why This Happens In development mode, we enable SharedArrayBuffer for fast SQLite operations, which requires: + - `Cross-Origin-Opener-Policy: same-origin` - `Cross-Origin-Embedder-Policy: require-corp` @@ -35,6 +36,7 @@ These headers create a **cross-origin isolated environment** that blocks resourc ### 1. Use Supported Image Hosting Services **Recommended services that work well:** + - **Imgur**: Free, no registration required, direct links - **GitHub**: If you have images in repositories - **Unsplash**: For stock photos @@ -45,6 +47,7 @@ These headers create a **cross-origin isolated environment** that blocks resourc If you frequently use images from a specific domain, add a proxy: #### Step 1: Add Proxy to `vite.config.common.mts` + ```typescript '/yourservice-proxy': { target: 'https://yourservice.com', @@ -63,6 +66,7 @@ If you frequently use images from a specific domain, add a proxy: ``` #### Step 2: Update Transform Function in `src/libs/util.ts` + ```typescript // Transform YourService URLs to use proxy if (imageUrl.startsWith("https://yourservice.com/")) { @@ -74,6 +78,7 @@ if (imageUrl.startsWith("https://yourservice.com/")) { ### 3. Use Alternative Image Sources For frequently failing domains, consider: + - Upload images to Imgur or GitHub - Use a CDN with proper CORS headers - Host images on your own domain with CORS enabled @@ -81,11 +86,13 @@ For frequently failing domains, consider: ## Development vs Production ### Development Mode + - Images from supported services work through proxies - Unsupported images may fail to load - Console warnings show which images have issues ### Production Mode + - All images load directly without proxies - No CORS restrictions in production - Better performance without proxy overhead @@ -93,6 +100,7 @@ For frequently failing domains, consider: ## Testing Image Sources ### Check if an Image Source Works + ```bash # Test in browser console: fetch('https://example.com/image.jpg', { mode: 'cors' }) @@ -101,6 +109,7 @@ fetch('https://example.com/image.jpg', { mode: 'cors' }) ``` ### Visual Testing + ```typescript import { createTestImageElements } from './libs/test-cors-images'; createTestImageElements(); // Creates visual test panel @@ -109,30 +118,36 @@ createTestImageElements(); // Creates visual test panel ## Common Error Messages ### `ERR_BLOCKED_BY_RESPONSE.NotSameOriginAfterDefaultedToSameOriginByCoep` + **Cause**: Image source doesn't send required CORS headers **Solution**: Use a supported image hosting service or add a proxy ### `ERR_NETWORK` or `ERR_INTERNET_DISCONNECTED` + **Cause**: Proxy service is unavailable **Solution**: Check internet connection or use alternative image source ### Images Load in Production but Not Development + **Cause**: Normal behavior - development has stricter CORS requirements **Solution**: Use supported image sources for development testing ## Best Practices ### For New Projects + 1. Use supported image hosting services from the start 2. Upload user images to Imgur or similar service 3. Host critical images on your own domain with CORS enabled ### For Existing Projects + 1. Identify frequently used image domains in console warnings 2. Add proxies for the most common domains 3. Gradually migrate to supported image hosting services ### For User-Generated Content + 1. Provide upload functionality to supported services 2. Validate image URLs against supported domains 3. Show helpful error messages for unsupported sources @@ -140,17 +155,20 @@ createTestImageElements(); // Creates visual test panel ## Troubleshooting ### Image Not Loading? + 1. Check browser console for error messages 2. Verify the domain is in the supported list 3. Test if the image loads in production mode 4. Consider adding a proxy for that domain ### Proxy Not Working? + 1. Check if the target service allows proxying 2. Verify CORS headers are being set correctly 3. Test with a simpler image URL from the same domain ### Performance Issues? + 1. Proxies add latency in development only 2. Production uses direct image loading 3. Consider using a local image cache for development @@ -158,6 +176,7 @@ createTestImageElements(); // Creates visual test panel ## Quick Fixes ### For Immediate Issues + ```typescript // Temporary fallback: disable CORS headers for testing // In vite.config.common.mts, comment out: @@ -166,9 +185,11 @@ createTestImageElements(); // Creates visual test panel // 'Cross-Origin-Embedder-Policy': 'require-corp' // }, ``` + **Note**: This disables SharedArrayBuffer performance benefits. ### For Long-term Solution + - Use supported image hosting services - Add proxies for frequently used domains - Migrate critical images to your own CORS-enabled CDN @@ -177,4 +198,4 @@ createTestImageElements(); // Creates visual test panel The cross-origin isolated environment is necessary for SharedArrayBuffer performance but requires careful image source management. Use the supported services, add proxies for common domains, and accept that some external images may not work in development mode. -This is a development-only limitation - production deployments work with any image source. \ No newline at end of file +This is a development-only limitation - production deployments work with any image source. diff --git a/doc/logging-configuration.md b/doc/logging-configuration.md index 2ef2a6d2..f0e14e9e 100644 --- a/doc/logging-configuration.md +++ b/doc/logging-configuration.md @@ -101,6 +101,7 @@ Database logging continues to work regardless of console log level settings. All ### No Logs Appearing Check your `VITE_LOG_LEVEL` setting: + ```bash echo $VITE_LOG_LEVEL ``` @@ -108,6 +109,7 @@ echo $VITE_LOG_LEVEL ### Too Many Logs Reduce verbosity by setting a lower log level: + ```bash VITE_LOG_LEVEL=warn ``` diff --git a/doc/meta_rule_usage_guide.md b/doc/meta_rule_usage_guide.md new file mode 100644 index 00000000..3b2a8251 --- /dev/null +++ b/doc/meta_rule_usage_guide.md @@ -0,0 +1,316 @@ +# Meta-Rule Usage Guide: How to Use Meta-Rules in Practice + +**Author**: Matthew Raymer +**Date**: 2025-08-21 +**Status**: 🎯 **ACTIVE** - Comprehensive meta-rule usage guide + +## Overview + +This guide explains how to use the TimeSafari meta-rule system in practice. +Meta-rules are high-level rule bundles that provide workflow-specific guidance +for different types of tasks. + +**Educational Goal**: Help developers understand when and how to apply +meta-rules to maximize their effectiveness and avoid common mistakes. + +## Why Meta-Rules Matter + +**Meta-rules solve the problem of rule overload** by bundling related rules +into logical workflows. Instead of manually selecting 10+ individual rules, +you apply 1-3 meta-rules that automatically include everything you need. + +### **Benefits of Using Meta-Rules** + +- **Faster Setup**: No need to manually select individual rules +- **Better Coverage**: Ensures you don't miss important rules +- **Workflow Consistency**: Standardized approaches across the team +- **Learning Efficiency**: Learn workflows, not individual rules +- **Quality Assurance**: Built-in validation and feedback mechanisms + +## Meta-Rule Selection Strategy + +### **Step 1: Always Start with Core Always-On** + +**Every single interaction** starts with: +``` +meta_core_always_on.mdc +``` + +This provides the foundation: human competence principles, time standards, +version control, and application context. + +### **Step 2: Identify Your Primary Task Type** + +Choose the meta-rule that matches your main objective: + +| **Task Type** | **Primary Meta-Rule** | **When to Use** | +|---------------|------------------------|------------------| +| **Research/Investigation** | `meta_research.mdc` | Bug diagnosis, feasibility research, code analysis | +| **Feature Planning** | `meta_feature_planning.mdc` | New feature design, requirements analysis | +| **Feature Implementation** | `meta_feature_implementation.mdc` | Building features, coding, testing | +| **Bug Diagnosis** | `meta_bug_diagnosis.mdc` | Investigating issues, root cause analysis | +| **Bug Fixing** | `meta_bug_fixing.mdc` | Implementing fixes, validation | +| **Documentation** | `meta_documentation.mdc` | Writing docs, creating guides, tutorials | + +### **Step 3: Add Context-Specific Meta-Rules (Optional)** + +For complex tasks, you might combine multiple meta-rules: + +``` +meta_core_always_on + meta_research + meta_bug_diagnosis +``` + +## Workflow Flexibility: Phase-Based, Not Waterfall + +**Important**: Meta-rules represent **workflow phases**, not a rigid sequence. You can: + +### **Jump Between Phases Freely** +- **Start with diagnosis** if you already know the problem +- **Go back to research** if your fix reveals new issues +- **Switch to planning** mid-implementation if scope changes +- **Document at any phase** - not just at the end + +### **Mode Switching by Invoking Meta-Rules** +Each meta-rule invocation **automatically switches your workflow mode**: + +``` +Research Mode → Invoke @meta_bug_diagnosis → Diagnosis Mode +Diagnosis Mode → Invoke @meta_bug_fixing → Fixing Mode +Planning Mode → Invoke @meta_feature_implementation → Implementation Mode +``` + +### **Phase Constraints, Not Sequence Constraints** +- **Within each phase**: Clear constraints on what you can/cannot do +- **Between phases**: Complete freedom to move as needed +- **No forced order**: Choose the phase that matches your current need + +### **Example of Flexible Workflow** +``` +1. Start with @meta_research (investigation mode) +2. Jump to @meta_bug_diagnosis (diagnosis mode) +3. Realize you need more research → back to @meta_research +4. Complete diagnosis → @meta_bug_fixing (implementation mode) +5. Find new issues → back to @meta_bug_diagnosis +6. Complete fix → @meta_documentation (documentation mode) +``` + +**The "sticky" part means**: Each phase has clear boundaries, but you control when to enter/exit phases. + +## Practical Usage Examples + +### **Example 1: Bug Investigation (Flexible Flow)** + +**Scenario**: User reports that the contact list isn't loading properly + +**Initial Meta-Rule Selection**: +``` +meta_core_always_on + meta_research + meta_bug_diagnosis +``` + +**What This Gives You**: +- **Core Always-On**: Human competence focus, time standards, context +- **Research**: Systematic investigation methodology, evidence collection +- **Bug Diagnosis**: Defect analysis framework, root cause identification + +**Flexible Workflow**: +1. Apply core always-on for foundation +2. Use research meta-rule for systematic investigation +3. Switch to bug diagnosis when you have enough evidence +4. **Can go back to research** if diagnosis reveals new questions +5. **Can jump to bug fixing** if root cause is obvious +6. **Can document findings** at any phase + +### **Example 2: Feature Development (Iterative Flow)** + +**Scenario**: Building a new contact search feature + +**Meta-Rule Selection**: +``` +meta_core_always_on + meta_feature_planning + meta_feature_implementation +``` + +**What This Gives You**: +- **Core Always-On**: Foundation principles and context +- **Feature Planning**: Requirements analysis, architecture planning +- **Feature Implementation**: Development workflow, testing strategy + +**Iterative Workflow**: +1. Start with core always-on +2. Use feature planning for design and requirements +3. Switch to feature implementation for coding and testing +4. **Can return to planning** if implementation reveals design issues +5. **Can go back to research** if you need to investigate alternatives +6. **Can document progress** throughout the process + +### **Example 3: Documentation Creation (Parallel Flow)** + +**Scenario**: Writing a migration guide for the new database system + +**Meta-Rule Selection**: +``` +meta_core_always_on + meta_documentation +``` + +**What This Gives You**: +- **Core Always-On**: Foundation and context +- **Documentation**: Educational focus, templates, quality standards + +**Parallel Workflow**: +1. Apply core always-on for foundation +2. Use documentation meta-rule for educational content creation +3. **Can research** while documenting if you need more information +4. **Can plan** documentation structure as you write +5. **Can implement** examples or code snippets as needed +6. Follow educational templates and quality standards + +## Meta-Rule Application Process + +### **1. Load the Meta-Rule** + +When you start a task, explicitly state which meta-rules you're applying: + +``` +"I'm applying meta_core_always_on + meta_research for this bug investigation." +``` + +### **2. Follow the Bundled Workflow** + +Each meta-rule provides a complete workflow. Follow it step-by-step: + +- **Research Meta-Rule**: Investigation → Evidence → Analysis → Conclusion +- **Feature Planning**: Requirements → Architecture → Strategy → Validation +- **Bug Diagnosis**: Problem → Evidence → Root Cause → Solution + +### **3. Use the Bundled Rules** + +Meta-rules automatically include all necessary sub-rules. You don't need to +manually select individual rules - they're already bundled. + +### **4. Validate Against Success Criteria** + +Each meta-rule includes success criteria. Use these to validate your work: + +- [ ] **Educational Quality**: Content increases human competence +- [ ] **Technical Quality**: All technical details are accurate +- [ ] **Workflow Completion**: All required steps completed +- [ ] **Quality Standards**: Meets defined quality criteria + +## Common Meta-Rule Combinations + +### **Research + Diagnosis** +``` +meta_core_always_on + meta_research + meta_bug_diagnosis +``` +**Use for**: Complex bug investigations requiring systematic analysis + +### **Planning + Implementation** +``` +meta_core_always_on + meta_feature_planning + meta_feature_implementation +``` +**Use for**: End-to-end feature development from concept to deployment + +### **Research + Planning** +``` +meta_core_always_on + meta_research + meta_feature_planning +``` +**Use for**: Feasibility research and solution design + +### **Documentation + Context** +``` +meta_core_always_on + meta_documentation + [context-specific] +``` +**Use for**: Creating comprehensive, educational documentation + +## Best Practices + +### **✅ Do These Things** + +- **Always start with core always-on** - it's the foundation +- **Choose the primary meta-rule** that matches your main task +- **Follow the bundled workflow** step-by-step +- **Use success criteria** to validate your work +- **Collect feedback** on meta-rule effectiveness + +### **❌ Avoid These Mistakes** + +- **Don't skip core always-on** - you'll lose the foundation +- **Don't apply too many meta-rules** - stick to 2-3 maximum +- **Don't ignore the bundled workflow** - follow it systematically +- **Don't forget validation** - use the success criteria +- **Don't skip feedback collection** - it improves the system + +## Troubleshooting Common Issues + +### **Problem**: Meta-rules seem to conflict + +**Solution**: Meta-rules are designed to work together. If you see conflicts, +you're probably applying too many. Stick to 2-3 meta-rules maximum. + +### **Problem**: I don't know which meta-rule to use + +**Solution**: Start with your primary task type. If you're investigating a bug, +use research + bug diagnosis. If you're building a feature, use feature +planning + implementation. + +### **Problem**: The meta-rule workflow seems too complex + +**Solution**: Meta-rules bundle complexity into manageable workflows. Follow +the steps one at a time. The complexity is already organized for you. + +### **Problem**: I'm not seeing the expected behavior + +**Solution**: Ensure you're following the meta-rule workflow step-by-step. +Meta-rules provide guidance, but you still need to execute the workflow. + +## Feedback and Improvement + +### **Rate Your Experience** + +After using a meta-rule, provide feedback: + +- **Effectiveness**: How well did the meta-rule work? (1-5 scale) +- **Time Saved**: How much time did it save you? +- **Quality Improvement**: Did it improve your work quality? +- **Recommendation**: Would you recommend it to others? + +### **Continuous Improvement** + +Meta-rules evolve based on feedback: + +- **Usage patterns** - How teams use the rules +- **Effectiveness ratings** - What works and what doesn't +- **Integration feedback** - How well rules work together +- **Quality metrics** - Impact on work quality + +## Quick Reference + +### **Meta-Rule Selection Matrix** + +| **Task** | **Primary** | **Secondary** | **Tertiary** | +|----------|-------------|---------------|---------------| +| **Bug Investigation** | `meta_research` | `meta_bug_diagnosis` | - | +| **Feature Development** | `meta_feature_planning` | `meta_feature_implementation` | - | +| **Documentation** | `meta_documentation` | - | - | +| **Complex Research** | `meta_research` | `meta_bug_diagnosis` | `meta_feature_planning` | + +### **Always Remember** + +1. **Start with core always-on** - foundation for everything +2. **Choose your primary meta-rule** - matches your main task +3. **Follow the bundled workflow** - step-by-step execution +4. **Validate against success criteria** - ensure quality +5. **Provide feedback** - help improve the system + +--- + +**See also**: + +- `.cursor/rules/meta_rule_architecture.md` for meta-rule structure overview +- `.cursor/rules/meta_core_always_on.mdc` for foundation rules +- `.cursor/rules/README.md` for complete rule organization + +**Status**: Active usage guide +**Priority**: High +**Estimated Effort**: Ongoing reference +**Dependencies**: All meta-rules +**Stakeholders**: Development team, Documentation team diff --git a/doc/migration-fence-definition.md b/doc/migration-fence-definition.md index fe0d7f5a..14341f6e 100644 --- a/doc/migration-fence-definition.md +++ b/doc/migration-fence-definition.md @@ -9,6 +9,7 @@ This document defines the **migration fence** - the boundary between the legacy ## Current Migration Status ### ✅ Completed Components + - **SQLite Database Service**: Fully implemented with absurd-sql - **Platform Service Layer**: Unified database interface across platforms - **PlatformServiceMixin**: Centralized database access with caching and utilities @@ -17,12 +18,14 @@ This document defines the **migration fence** - the boundary between the legacy - **Data Export/Import**: Backup and restore functionality ### 🔄 Active Migration Components + - **Settings Migration**: Core user settings transferred - **Account Migration**: Identity and key management - **Contact Migration**: User contact data (via import interface) - **DatabaseUtil Migration**: Moving functions to PlatformServiceMixin ### ❌ Legacy Components (Fence Boundary) + - **Dexie Database**: Legacy IndexedDB storage (disabled by default) - **Dexie-Specific Code**: Direct database access patterns - **Legacy Migration Paths**: Old data transfer methods @@ -45,6 +48,7 @@ export const PlatformServiceMixin = { ``` **Fence Rule**: All database operations must use: + - `this.$db()` for read operations - `this.$exec()` for write operations - `this.$settings()` for settings access @@ -64,6 +68,7 @@ export class PlatformServiceFactory { ``` **Fence Rule**: All database operations must use: + - `PlatformService.dbQuery()` for read operations - `PlatformService.dbExec()` for write operations - No direct `db.` or `accountsDBPromise` access in application code @@ -71,6 +76,7 @@ export class PlatformServiceFactory { ### 3. Data Access Patterns #### ✅ Allowed (Inside Fence) + ```typescript // Use PlatformServiceMixin for all database operations const contacts = await this.$contacts(); @@ -79,6 +85,7 @@ const result = await this.$db("SELECT * FROM contacts WHERE did = ?", [accountDi ``` #### ❌ Forbidden (Outside Fence) + ```typescript // Direct Dexie access (legacy pattern) const contacts = await db.contacts.where('did').equals(accountDid).toArray(); @@ -98,6 +105,7 @@ export async function compareDatabases(): Promise<DataComparison> { ``` **Fence Rule**: Migration tools are the exclusive interface between: + - Legacy Dexie database - New SQLite database - Data comparison and transfer operations @@ -107,11 +115,13 @@ export async function compareDatabases(): Promise<DataComparison> { ### 1. Code Development Rules #### New Feature Development + - **Always** use `PlatformServiceMixin` for database operations - **Never** import or reference Dexie directly - **Always** use mixin methods like `this.$settings()`, `this.$contacts()` #### Legacy Code Maintenance + - **Only** modify Dexie code for migration purposes - **Always** add migration tests for schema changes - **Never** add new Dexie-specific features @@ -119,11 +129,13 @@ export async function compareDatabases(): Promise<DataComparison> { ### 2. Data Integrity Rules #### Migration Safety + - **Always** create backups before migration - **Always** verify data integrity after migration - **Never** delete legacy data until verified #### Rollback Strategy + - **Always** maintain ability to rollback to Dexie - **Always** preserve migration logs - **Never** assume migration is irreversible @@ -131,6 +143,7 @@ export async function compareDatabases(): Promise<DataComparison> { ### 3. Testing Requirements #### Migration Testing + ```typescript // Required test pattern for migration describe('Database Migration', () => { @@ -144,6 +157,7 @@ describe('Database Migration', () => { ``` #### Application Testing + ```typescript // Required test pattern for application features describe('Feature with Database', () => { @@ -159,6 +173,7 @@ describe('Feature with Database', () => { ### 1. Static Analysis #### ESLint Rules + ```json { "rules": { @@ -178,6 +193,7 @@ describe('Feature with Database', () => { ``` #### TypeScript Rules + ```json { "compilerOptions": { @@ -190,6 +206,7 @@ describe('Feature with Database', () => { ### 2. Runtime Checks #### Development Mode Validation + ```typescript // Development-only fence validation if (import.meta.env.DEV) { @@ -198,6 +215,7 @@ if (import.meta.env.DEV) { ``` #### Production Safety + ```typescript // Production fence enforcement if (import.meta.env.PROD) { @@ -209,6 +227,7 @@ if (import.meta.env.PROD) { ## Migration Status Checklist ### ✅ Completed + - [x] PlatformServiceMixin implementation - [x] SQLite database service - [x] Migration tools @@ -217,11 +236,13 @@ if (import.meta.env.PROD) { - [x] ActiveDid migration ### 🔄 In Progress + - [ ] Contact migration - [ ] DatabaseUtil to PlatformServiceMixin migration - [ ] File-by-file migration ### ❌ Not Started + - [ ] Legacy Dexie removal - [ ] Final cleanup and validation @@ -240,4 +261,4 @@ if (import.meta.env.PROD) { **Created**: 2025-07-05 **Status**: Active Migration Phase **Last Updated**: 2025-07-05 -**Note**: Migration fence now implemented through PlatformServiceMixin instead of USE_DEXIE_DB constant \ No newline at end of file +**Note**: Migration fence now implemented through PlatformServiceMixin instead of USE_DEXIE_DB constant diff --git a/doc/migration-progress-tracker.md b/doc/migration-progress-tracker.md index 41c2c592..63c190d7 100644 --- a/doc/migration-progress-tracker.md +++ b/doc/migration-progress-tracker.md @@ -3,6 +3,7 @@ ## Per-File Migration Workflow (MANDATORY) For each file migrated: + 1. **First**, migrate to PlatformServiceMixin (replace all databaseUtil usage, etc.). 2. **Immediately after**, standardize notify helper usage (property + created() pattern) and fix any related linter/type errors. @@ -25,22 +26,26 @@ This document tracks the progress of the 2-day sprint to complete PlatformServic ## ✅ **DAY 1: PlatformServiceMixin Completion (COMPLETE)** ### **Phase 1: Remove Circular Dependency (COMPLETE)** + **Status**: ✅ **COMPLETE** **Issue**: PlatformServiceMixin imports `memoryLogs` from databaseUtil **Solution**: Create self-contained memoryLogs implementation -#### **Tasks**: +#### **Tasks** + - [x] **Step 1.1**: Remove `memoryLogs` import from PlatformServiceMixin.ts ✅ - [x] **Step 1.2**: Add self-contained `_memoryLogs` array to PlatformServiceMixin ✅ - [x] **Step 1.3**: Add `$appendToMemoryLogs()` method to PlatformServiceMixin ✅ - [x] **Step 1.4**: Update logger.ts to use self-contained memoryLogs ✅ - [x] **Step 1.5**: Test memoryLogs functionality ✅ -#### **Files Modified**: +#### **Files Modified** + - `src/utils/PlatformServiceMixin.ts` ✅ - `src/utils/logger.ts` ✅ -#### **Validation**: +#### **Validation** + - [x] No circular dependency errors ✅ - [x] memoryLogs functionality works correctly ✅ - [x] Linting passes ✅ @@ -48,20 +53,24 @@ This document tracks the progress of the 2-day sprint to complete PlatformServic --- ### **Phase 2: Add Missing Utility Functions (COMPLETE)** + **Status**: ✅ **COMPLETE** **Missing Functions**: `generateInsertStatement`, `generateUpdateStatement` -#### **Tasks**: +#### **Tasks** + - [x] **Step 2.1**: Add `_generateInsertStatement()` private method to PlatformServiceMixin ✅ - [x] **Step 2.2**: Add `_generateUpdateStatement()` private method to PlatformServiceMixin ✅ - [x] **Step 2.3**: Add `$generateInsertStatement()` public wrapper method ✅ - [x] **Step 2.4**: Add `$generateUpdateStatement()` public wrapper method ✅ - [x] **Step 2.5**: Test both utility functions ✅ -#### **Files Modified**: +#### **Files Modified** + - `src/utils/PlatformServiceMixin.ts` ✅ -#### **Validation**: +#### **Validation** + - [x] Both functions generate correct SQL ✅ - [x] Parameter handling works correctly ✅ - [x] Type safety maintained ✅ @@ -69,18 +78,22 @@ This document tracks the progress of the 2-day sprint to complete PlatformServic --- ### **Phase 3: Update Type Definitions (COMPLETE)** + **Status**: ✅ **COMPLETE** **Goal**: Add new methods to TypeScript interfaces -#### **Tasks**: +#### **Tasks** + - [x] **Step 3.1**: Add new methods to `IPlatformServiceMixin` interface ✅ - [x] **Step 3.2**: Add new methods to `ComponentCustomProperties` interface ✅ - [x] **Step 3.3**: Verify TypeScript compilation ✅ -#### **Files Modified**: +#### **Files Modified** + - `src/utils/PlatformServiceMixin.ts` (interface definitions) ✅ -#### **Validation**: +#### **Validation** + - [x] TypeScript compilation passes ✅ - [x] All new methods properly typed ✅ - [x] No type errors in existing code ✅ @@ -88,17 +101,20 @@ This document tracks the progress of the 2-day sprint to complete PlatformServic --- ### **Phase 4: Testing & Validation (COMPLETE)** + **Status**: ✅ **COMPLETE** **Goal**: Ensure PlatformServiceMixin is fully functional -#### **Tasks**: +#### **Tasks** + - [x] **Step 4.1**: Create test component to verify all methods ✅ - [x] **Step 4.2**: Run comprehensive linting ✅ - [x] **Step 4.3**: Run TypeScript type checking ✅ - [x] **Step 4.4**: Test caching functionality ✅ - [x] **Step 4.5**: Test database operations ✅ -#### **Validation**: +#### **Validation** + - [x] All tests pass ✅ - [x] No linting errors ✅ - [x] No TypeScript errors ✅ @@ -108,10 +124,12 @@ This document tracks the progress of the 2-day sprint to complete PlatformServic --- ### **Phase 5: Utility Files Migration (COMPLETE)** + **Status**: ✅ **COMPLETE** **Goal**: Remove all remaining databaseUtil imports from utility files -#### **Tasks**: +#### **Tasks** + - [x] **Step 5.1**: Migrate `src/services/deepLinks.ts` ✅ - Replaced `logConsoleAndDb` with `console.error` - Removed databaseUtil import @@ -121,7 +139,8 @@ This document tracks the progress of the 2-day sprint to complete PlatformServic - Updated all async calls to use proper async pattern - [x] **Step 5.3**: Verify no remaining databaseUtil imports ✅ -#### **Validation**: +#### **Validation** + - [x] No databaseUtil imports in any TypeScript files ✅ - [x] No databaseUtil imports in any Vue files ✅ - [x] All functions work correctly ✅ @@ -131,13 +150,16 @@ This document tracks the progress of the 2-day sprint to complete PlatformServic ## 🎯 **DAY 2: Migrate All 52 Files (READY TO START)** ### **Migration Strategy** + **Priority Order**: + 1. **Views** (25 files) - User-facing components 2. **Components** (15 files) - Reusable UI components 3. **Services** (8 files) - Business logic 4. **Utils** (4 files) - Utility functions ### **Migration Pattern for Each File** + ```typescript // 1. Add PlatformServiceMixin import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; @@ -155,6 +177,7 @@ export default class ComponentName extends Vue { ``` ### **Common Replacements** + - `generateInsertStatement` → `this.$generateInsertStatement` - `generateUpdateStatement` → `this.$generateUpdateStatement` - `parseJsonField` → `this._parseJsonField` @@ -168,6 +191,7 @@ export default class ComponentName extends Vue { ## 📋 **File Migration Checklist** ### **Views (25 files) - Priority 1** + **Progress**: 6/25 (24%) - [ ] QuickActionBvcEndView.vue @@ -209,6 +233,7 @@ export default class ComponentName extends Vue { - [ ] UserProfileView.vue ### **Components (15 files) - Priority 2** + **Progress**: 9/15 (60%) - [x] UserNameDialog.vue ✅ **MIGRATED** @@ -233,6 +258,7 @@ export default class ComponentName extends Vue { - [x] IconRenderer.vue ✅ MIGRATED & HUMAN TESTED 2024-12-19 (0 min, no migration needed - already compliant) ### **Services (8 files) - Priority 3** + **Progress**: 2/8 (25%) - [x] api.ts ✅ MIGRATED 2024-12-19 (0 min, no migration needed - already compliant) @@ -241,6 +267,7 @@ export default class ComponentName extends Vue { - [ ] deepLinks.ts ### **Utils (4 files) - Priority 4** + **Progress**: 1/4 (25%) - [ ] LogCollector.ts @@ -253,6 +280,7 @@ export default class ComponentName extends Vue { ## 🛠️ **Migration Tools** ### **Migration Helper Script** + ```bash # Track progress ./scripts/migration-helper.sh progress @@ -277,6 +305,7 @@ export default class ComponentName extends Vue { ``` ### **Validation Commands** + ```bash # Check for remaining databaseUtil imports find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" @@ -296,12 +325,14 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | ## 📊 **Progress Tracking** ### **Day 1 Progress** + - [ ] Phase 1: Circular dependency resolved - [ ] Phase 2: Utility functions added - [ ] Phase 3: Type definitions updated - [ ] Phase 4: Testing completed ### **Day 2 Progress** + - [ ] Views migrated (0/25) - [ ] Components migrated (0/15) - [ ] Services migrated (0/8) @@ -309,6 +340,7 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | - [ ] Validation completed ### **Overall Progress** + - **Total files to migrate**: 52 - **Files migrated**: 3 - **Progress**: 6% @@ -318,6 +350,7 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | ## 🎯 **Success Criteria** ### **Day 1 Success Criteria** + - [ ] PlatformServiceMixin has no circular dependencies - [ ] All utility functions implemented and tested - [ ] Type definitions complete and accurate @@ -325,6 +358,7 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | - [ ] TypeScript compilation passes ### **Day 2 Success Criteria** + - [ ] 0 files importing databaseUtil - [ ] All 52 files migrated to PlatformServiceMixin - [ ] No runtime errors in migrated components @@ -332,6 +366,7 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | - [ ] Performance maintained or improved ### **Overall Success Criteria** + - [ ] Complete elimination of databaseUtil dependency - [ ] PlatformServiceMixin is the single source of truth for database operations - [ ] Migration fence is fully implemented @@ -354,14 +389,17 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | ## 📝 **Notes & Issues** ### **Current Issues** + - None identified yet ### **Decisions Made** + - PlatformServiceMixin approach chosen over USE_DEXIE_DB constant - Self-contained utility functions preferred over imports - Priority order: Views → Components → Services → Utils ### **Lessons Learned** + - To be filled as migration progresses --- @@ -369,6 +407,7 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | ## 🔄 **Daily Updates** ### **Day 1 Updates** + - [ ] Start time: _____ - [ ] Phase 1 completion: _____ - [ ] Phase 2 completion: _____ @@ -377,6 +416,7 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | - [ ] End time: _____ ### **Day 2 Updates** + - [ ] Start time: _____ - [ ] Views migration completion: _____ - [ ] Components migration completion: _____ @@ -390,16 +430,19 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | ## 🆘 **Contingency Plans** ### **If Day 1 Takes Longer** + - Focus on core functionality first - Defer advanced utility functions to Day 2 - Prioritize circular dependency resolution ### **If Day 2 Takes Longer** + - Focus on high-impact views first - Batch similar components together - Use automated scripts for common patterns ### **If Issues Arise** + - Document specific problems in Notes section - Create targeted fixes - Maintain backward compatibility during transition @@ -421,4 +464,4 @@ These practices ensure maintainability, consistency, and type safety for all not --- **Last Updated**: $(date) -**Next Review**: After each phase completion \ No newline at end of file +**Next Review**: After each phase completion diff --git a/doc/migration-quick-reference.md b/doc/migration-quick-reference.md index 5f84bfaa..3e77dc8a 100644 --- a/doc/migration-quick-reference.md +++ b/doc/migration-quick-reference.md @@ -63,6 +63,7 @@ export default class ComponentName extends Vue { ## ✅ **Validation Checklist** After each file migration: + - [ ] No databaseUtil imports - [ ] PlatformServiceMixin added - [ ] Method calls updated @@ -91,4 +92,4 @@ npm run lint && npx tsc --noEmit --- **Last Updated**: $(date) -**Full Documentation**: `doc/migration-progress-tracker.md` \ No newline at end of file +**Full Documentation**: `doc/migration-progress-tracker.md` diff --git a/doc/migration-readiness-summary.md b/doc/migration-readiness-summary.md index a743257c..6797b0d7 100644 --- a/doc/migration-readiness-summary.md +++ b/doc/migration-readiness-summary.md @@ -11,11 +11,14 @@ ## 🎯 **Migration Overview** ### **Goal** + Complete the TimeSafari database migration from Dexie to SQLite by: + 1. **Day 1**: Finish PlatformServiceMixin implementation (4-6 hours) 2. **Day 2**: Migrate all 52 files to PlatformServiceMixin (6-8 hours) ### **Current Status** + - ✅ **PlatformServiceMixin**: 95% complete (1,301 lines) - ✅ **Migration Tools**: Ready and tested - ✅ **Documentation**: Complete and cross-machine accessible @@ -27,22 +30,30 @@ Complete the TimeSafari database migration from Dexie to SQLite by: ## 📊 **File Breakdown** ### **Views (42 files) - Priority 1** + User-facing components that need immediate attention: + - 25 files from original list - 17 additional files identified by migration helper ### **Components (9 files) - Priority 2** + Reusable UI components: + - FeedFilters.vue, GiftedDialog.vue, GiftedPrompts.vue - ImageMethodDialog.vue, OfferDialog.vue, OnboardingDialog.vue - PhotoDialog.vue, PushNotificationPermission.vue, UserNameDialog.vue ### **Services (1 file) - Priority 3** + Business logic: + - deepLinks.ts ### **Utils (3 files) - Priority 4** + Utility functions: + - util.ts, test/index.ts, PlatformServiceMixin.ts (circular dependency fix) --- @@ -50,17 +61,21 @@ Utility functions: ## 🛠️ **Available Tools** ### **Migration Helper Script** + ```bash ./scripts/migration-helper.sh [command] ``` + **Commands**: progress, files, patterns, template, validate, next, all ### **Progress Tracking** + - **Main Tracker**: `doc/migration-progress-tracker.md` - **Quick Reference**: `doc/migration-quick-reference.md` - **Completion Plan**: `doc/platformservicemixin-completion-plan.md` ### **Validation Commands** + ```bash # Check progress ./scripts/migration-helper.sh progress @@ -77,6 +92,7 @@ find src -name "*.vue" -o -name "*.ts" | xargs grep -l "import.*databaseUtil" | ## 🔄 **Migration Pattern** ### **Standard Template** + ```typescript // 1. Add import import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; @@ -94,6 +110,7 @@ export default class ComponentName extends Vue { ``` ### **Common Replacements** + | Old | New | |-----|-----| | `generateInsertStatement` | `this.$generateInsertStatement` | @@ -109,19 +126,23 @@ export default class ComponentName extends Vue { ## 🎯 **Day 1 Plan: PlatformServiceMixin Completion** ### **Phase 1: Remove Circular Dependency (30 min)** + - Remove `memoryLogs` import from PlatformServiceMixin - Add self-contained memoryLogs implementation - Update logger.ts ### **Phase 2: Add Missing Functions (1 hour)** + - Add `generateInsertStatement` and `generateUpdateStatement` - Test both utility functions ### **Phase 3: Update Types (30 min)** + - Add new methods to TypeScript interfaces - Verify compilation ### **Phase 4: Testing (1 hour)** + - Comprehensive testing and validation - Ensure no circular dependencies @@ -130,17 +151,20 @@ export default class ComponentName extends Vue { ## 🎯 **Day 2 Plan: File Migration** ### **Strategy** + 1. **Views First** (42 files) - High impact, user-facing 2. **Components** (9 files) - Reusable UI elements 3. **Services** (1 file) - Business logic 4. **Utils** (3 files) - Utility functions ### **Batch Processing** + - Process similar files together - Use automated scripts for common patterns - Validate after each batch ### **Success Criteria** + - 0 files importing databaseUtil - All tests passing - No runtime errors @@ -151,12 +175,14 @@ export default class ComponentName extends Vue { ## 🚀 **Expected Benefits** ### **Immediate Benefits** + - **80% reduction** in database boilerplate code - **Eliminated circular dependencies** - **Centralized caching** for performance - **Type-safe** database operations ### **Long-term Benefits** + - **Simplified testing** with mockable mixin - **Consistent error handling** across components - **Ready for SQLite-only mode** @@ -167,18 +193,21 @@ export default class ComponentName extends Vue { ## 📋 **Pre-Migration Checklist** ### **Environment Ready** + - [x] Migration helper script tested and working - [x] Progress tracking system operational - [x] Documentation complete and accessible - [x] Validation commands working ### **Tools Available** + - [x] Automated progress tracking - [x] Migration pattern templates - [x] Validation scripts - [x] Cross-machine documentation ### **Knowledge Base** + - [x] Common replacement patterns documented - [x] Migration templates ready - [x] Troubleshooting guides available @@ -191,12 +220,14 @@ export default class ComponentName extends Vue { **All systems are ready for the 2-day migration sprint.** ### **Next Steps** + 1. **Start Day 1**: Complete PlatformServiceMixin 2. **Use tracking tools**: Monitor progress with helper script 3. **Follow documentation**: Use provided templates and patterns 4. **Validate frequently**: Run checks after each phase ### **Success Metrics** + - **Day 1**: PlatformServiceMixin 100% complete, no circular dependencies - **Day 2**: 0 files importing databaseUtil, all tests passing - **Overall**: Ready for Phase 3 cleanup and optimization @@ -210,4 +241,4 @@ export default class ComponentName extends Vue { --- **Last Updated**: $(date) -**Next Review**: After Day 1 completion \ No newline at end of file +**Next Review**: After Day 1 completion diff --git a/doc/migration-roadmap-next-steps.md b/doc/migration-roadmap-next-steps.md index 995308d8..994d67c1 100644 --- a/doc/migration-roadmap-next-steps.md +++ b/doc/migration-roadmap-next-steps.md @@ -7,6 +7,7 @@ This document outlines the immediate next steps for completing the TimeSafari da ## Current Status Summary ### ✅ **Completed Achievements** + 1. **Circular Dependencies Resolved** - No active circular dependencies blocking development 2. **PlatformServiceMixin Implemented** - Core functionality with caching and utilities 3. **Migration Tools Ready** - Data comparison and transfer utilities functional @@ -14,6 +15,7 @@ This document outlines the immediate next steps for completing the TimeSafari da 5. **Documentation Updated** - All docs reflect current PlatformServiceMixin approach ### 🔄 **Current Phase: Phase 2 - Active Migration** + - **DatabaseUtil Migration**: 52 files still importing databaseUtil - **Contact Migration**: Framework ready, implementation in progress - **File-by-File Migration**: Ready to begin systematic migration @@ -23,6 +25,7 @@ This document outlines the immediate next steps for completing the TimeSafari da ### 🔴 **Priority 1: Complete PlatformServiceMixin Independence** #### **Step 1.1: Remove memoryLogs Dependency** + ```typescript // Current: PlatformServiceMixin imports from databaseUtil import { memoryLogs } from "@/db/databaseUtil"; @@ -32,12 +35,15 @@ const memoryLogs: string[] = []; ``` **Files to modify**: + - `src/utils/PlatformServiceMixin.ts` - Remove import, add self-contained implementation **Estimated time**: 30 minutes #### **Step 1.2: Add Missing Utility Methods** + Add these methods to PlatformServiceMixin: + - `$parseJson()` - Self-contained JSON parsing - `$generateInsertStatement()` - SQL generation - `$generateUpdateStatement()` - SQL generation @@ -48,6 +54,7 @@ Add these methods to PlatformServiceMixin: ### 🟡 **Priority 2: Start File-by-File Migration** #### **Step 2.1: Migrate Critical Files First** + Based on the migration plan, start with these high-priority files: 1. **`src/App.vue`** - Main application (highest impact) @@ -57,6 +64,7 @@ Based on the migration plan, start with these high-priority files: 5. **`src/services/deepLinks.ts`** - Service layer **Migration pattern for each file**: + ```typescript // 1. Remove databaseUtil import // Remove: import * as databaseUtil from "../db/databaseUtil"; @@ -82,7 +90,9 @@ Based on the migration plan, start with these high-priority files: ### 🟡 **Priority 3: Systematic File Migration** #### **Step 3.1: Migrate High-Usage Components (15 files)** + Target components with databaseUtil imports: + - `PhotoDialog.vue` - `FeedFilters.vue` - `UserNameDialog.vue` @@ -97,7 +107,9 @@ Target components with databaseUtil imports: **Estimated time**: 15-30 hours #### **Step 3.2: Migrate High-Usage Views (20 files)** + Target views with databaseUtil imports: + - `IdentitySwitcherView.vue` - `ContactEditView.vue` - `ContactGiftingView.vue` @@ -113,6 +125,7 @@ Target views with databaseUtil imports: **Estimated time**: 20-40 hours #### **Step 3.3: Migrate Remaining Files (27 files)** + Complete migration of all remaining files with databaseUtil imports. **Estimated time**: 27-54 hours @@ -120,6 +133,7 @@ Complete migration of all remaining files with databaseUtil imports. ### 🟢 **Priority 4: Contact Migration Completion** #### **Step 4.1: Complete Contact Migration Framework** + - Implement contact import/export functionality - Add contact validation and error handling - Test contact migration with real data @@ -127,6 +141,7 @@ Complete migration of all remaining files with databaseUtil imports. **Estimated time**: 4-8 hours #### **Step 4.2: User Testing and Validation** + - Test migration with various data scenarios - Validate data integrity after migration - Performance testing with large datasets @@ -138,7 +153,9 @@ Complete migration of all remaining files with databaseUtil imports. ### 🔵 **Priority 5: Cleanup and Optimization** #### **Step 5.1: Remove Unused databaseUtil Functions** + After all files are migrated: + - Remove unused functions from databaseUtil.ts - Update TypeScript interfaces - Clean up legacy code @@ -146,6 +163,7 @@ After all files are migrated: **Estimated time**: 4-8 hours #### **Step 5.2: Performance Optimization** + - Optimize PlatformServiceMixin caching - Add performance monitoring - Implement database query optimization @@ -153,6 +171,7 @@ After all files are migrated: **Estimated time**: 8-16 hours #### **Step 5.3: Legacy Dexie Removal** + - Remove Dexie dependencies - Clean up migration tools - Update build configurations @@ -162,6 +181,7 @@ After all files are migrated: ## Migration Commands and Tools ### **Automated Migration Script** + Create a script to help with bulk migrations: ```bash @@ -193,6 +213,7 @@ echo "Please review and test the changes" ``` ### **Migration Testing Commands** + ```bash # Test individual file migration npm run test -- --grep "ComponentName" @@ -213,18 +234,21 @@ npx tsc --noEmit ## Risk Mitigation ### **Incremental Migration Strategy** + 1. **One file at a time** - Minimize risk of breaking changes 2. **Comprehensive testing** - Test each migration thoroughly 3. **Rollback capability** - Keep databaseUtil.ts until migration complete 4. **Documentation updates** - Update docs as methods are migrated ### **Testing Strategy** + 1. **Unit tests** - Test individual component functionality 2. **Integration tests** - Test database operations 3. **End-to-end tests** - Test complete user workflows 4. **Performance tests** - Ensure no performance regression ### **Rollback Plan** + 1. **Git branches** - Each migration in separate branch 2. **Backup files** - Keep original files until migration verified 3. **Feature flags** - Ability to switch back to databaseUtil if needed @@ -233,18 +257,21 @@ npx tsc --noEmit ## Success Metrics ### **Short-Term (This Week)** + - [ ] PlatformServiceMixin completely independent - [ ] 5 critical files migrated - [ ] No new circular dependencies - [ ] All tests passing ### **Medium-Term (Next 2 Weeks)** + - [ ] 35+ files migrated (70% completion) - [ ] Contact migration framework complete - [ ] Performance maintained or improved - [ ] User testing completed ### **Long-Term (Next Month)** + - [ ] All 52 files migrated (100% completion) - [ ] databaseUtil.ts removed or minimal - [ ] Legacy Dexie code removed @@ -253,12 +280,14 @@ npx tsc --noEmit ## Resource Requirements ### **Development Time** + - **Immediate (This Week)**: 8-12 hours - **Medium-Term (Next 2 Weeks)**: 35-70 hours - **Long-Term (Next Month)**: 16-32 hours - **Total Estimated**: 59-114 hours ### **Testing Time** + - **Unit Testing**: 20-30 hours - **Integration Testing**: 10-15 hours - **User Testing**: 8-12 hours @@ -266,6 +295,7 @@ npx tsc --noEmit - **Total Testing**: 43-65 hours ### **Total Project Time** + - **Development**: 59-114 hours - **Testing**: 43-65 hours - **Documentation**: 5-10 hours @@ -274,6 +304,7 @@ npx tsc --noEmit ## Conclusion The migration is well-positioned for completion with: + - ✅ **No blocking circular dependencies** - ✅ **PlatformServiceMixin mostly complete** - ✅ **Clear migration path defined** @@ -287,4 +318,4 @@ The next steps focus on systematic file-by-file migration with proper testing an **Created**: 2025-07-05 **Status**: Active Planning **Last Updated**: 2025-07-05 -**Note**: This roadmap is based on current codebase analysis and documented progress \ No newline at end of file +**Note**: This roadmap is based on current codebase analysis and documented progress diff --git a/doc/migration-security-checklist.md b/doc/migration-security-checklist.md index da219b69..501953f5 100644 --- a/doc/migration-security-checklist.md +++ b/doc/migration-security-checklist.md @@ -352,4 +352,4 @@ This security audit checklist ensures that the database migration maintains the **Reviewed By**: _______________ -**Approved By**: _______________ \ No newline at end of file +**Approved By**: _______________ diff --git a/doc/migration-to-wa-sqlite.md b/doc/migration-to-wa-sqlite.md index 34f6a632..eac594ab 100644 --- a/doc/migration-to-wa-sqlite.md +++ b/doc/migration-to-wa-sqlite.md @@ -29,12 +29,15 @@ This document outlines the migration process from Dexie.js to absurd-sql for the ## Migration Architecture ### Migration Fence + The migration fence is now defined by the **PlatformServiceMixin** in `src/utils/PlatformServiceMixin.ts`: + - **PlatformServiceMixin**: Centralized database access with caching and utilities - **Migration Tools**: Exclusive interface between legacy and new databases - **Service Layer**: All database operations go through PlatformService ### Migration Order + The migration follows a specific order to maintain data integrity: 1. **Accounts** (foundational - contains DIDs) @@ -45,9 +48,11 @@ The migration follows a specific order to maintain data integrity: ## ActiveDid Migration ⭐ **NEW FEATURE** ### Problem Solved + Previously, the `activeDid` setting was not migrated from Dexie to SQLite, causing users to lose their active identity after migration. ### Solution Implemented + The migration now includes a dedicated step for migrating the `activeDid`: 1. **Detection**: Identifies the `activeDid` from Dexie master settings @@ -58,6 +63,7 @@ The migration now includes a dedicated step for migrating the `activeDid`: ### Implementation Details #### New Function: `migrateActiveDid()` + ```typescript export async function migrateActiveDid(): Promise<MigrationResult> { // 1. Get Dexie settings to find the activeDid @@ -76,13 +82,17 @@ export async function migrateActiveDid(): Promise<MigrationResult> { ``` #### Enhanced `migrateSettings()` Function + The settings migration now includes activeDid handling: + - Extracts `activeDid` from Dexie master settings - Validates account existence in SQLite - Updates SQLite master settings with the `activeDid` #### Updated `migrateAll()` Function + The complete migration now includes a dedicated step for activeDid: + ```typescript // Step 3: Migrate ActiveDid (depends on accounts and settings) logger.info("[MigrationService] Step 3: Migrating activeDid..."); @@ -90,6 +100,7 @@ const activeDidResult = await migrateActiveDid(); ``` ### Benefits + - ✅ **User Identity Preservation**: Users maintain their active identity - ✅ **Seamless Experience**: No need to manually select identity after migration - ✅ **Data Consistency**: Ensures all identity-related settings are preserved @@ -98,17 +109,20 @@ const activeDidResult = await migrateActiveDid(); ## Migration Process ### Phase 1: Preparation ✅ + - [x] PlatformServiceMixin implementation - [x] Implement data comparison tools - [x] Create migration service structure ### Phase 2: Core Migration ✅ + - [x] Account migration with `importFromMnemonic` - [x] Settings migration (excluding activeDid) - [x] **ActiveDid migration** ⭐ **COMPLETED** - [x] Contact migration framework ### Phase 3: Validation and Cleanup 🔄 + - [ ] Comprehensive data validation - [ ] Performance testing - [ ] User acceptance testing @@ -117,6 +131,7 @@ const activeDidResult = await migrateActiveDid(); ## Usage ### Manual Migration + ```typescript import { migrateAll, migrateActiveDid } from '../services/indexedDBMigrationService'; @@ -128,6 +143,7 @@ const activeDidResult = await migrateActiveDid(); ``` ### Migration Verification + ```typescript import { compareDatabases } from '../services/indexedDBMigrationService'; @@ -136,7 +152,9 @@ console.log('Migration differences:', comparison.differences); ``` ### PlatformServiceMixin Integration + After migration, use the mixin for all database operations: + ```typescript // Use mixin methods for database access const contacts = await this.$contacts(); @@ -147,11 +165,13 @@ const result = await this.$db("SELECT * FROM contacts WHERE did = ?", [accountDi ## Error Handling ### ActiveDid Migration Errors + - **Missing Account**: If the `activeDid` from Dexie doesn't exist in SQLite accounts - **Database Errors**: Connection or query failures - **Settings Update Failures**: Issues updating SQLite master settings ### Recovery Strategies + 1. **Automatic Recovery**: Migration continues even if activeDid migration fails 2. **Manual Recovery**: Users can manually select their identity after migration 3. **Fallback**: System creates new identity if none exists @@ -159,11 +179,13 @@ const result = await this.$db("SELECT * FROM contacts WHERE did = ?", [accountDi ## Security Considerations ### Data Protection + - All sensitive data (mnemonics, private keys) are encrypted - Migration preserves encryption standards - No plaintext data exposure during migration ### Identity Verification + - ActiveDid migration validates account existence - Prevents setting non-existent identities as active - Maintains cryptographic integrity @@ -171,6 +193,7 @@ const result = await this.$db("SELECT * FROM contacts WHERE did = ?", [accountDi ## Testing ### Migration Testing + ```bash # Run migration npm run migrate @@ -180,6 +203,7 @@ npm run test:migration ``` ### ActiveDid Testing + ```typescript // Test activeDid migration specifically const result = await migrateActiveDid(); @@ -188,6 +212,7 @@ expect(result.warnings).toContain('Successfully migrated activeDid'); ``` ### PlatformServiceMixin Testing + ```typescript // Test mixin integration describe('PlatformServiceMixin', () => { @@ -224,6 +249,7 @@ describe('PlatformServiceMixin', () => { - Verify caching and error handling work correctly ### Debugging + ```typescript // Debug migration process import { logger } from '../utils/logger'; @@ -245,6 +271,7 @@ logger.debug('[Migration] Migration completed:', result); ## Migration Status Checklist ### ✅ Completed + - [x] PlatformServiceMixin implementation - [x] SQLite database service - [x] Migration tools @@ -253,11 +280,13 @@ logger.debug('[Migration] Migration completed:', result); - [x] ActiveDid migration ### 🔄 In Progress + - [ ] Contact migration - [ ] DatabaseUtil to PlatformServiceMixin migration - [ ] File-by-file migration ### ❌ Not Started + - [ ] Legacy Dexie removal - [ ] Final cleanup and validation @@ -267,4 +296,4 @@ logger.debug('[Migration] Migration completed:', result); **Created**: 2025-07-05 **Status**: Active Migration Phase **Last Updated**: 2025-07-05 -**Note**: Migration fence now implemented through PlatformServiceMixin instead of USE_DEXIE_DB constant \ No newline at end of file +**Note**: Migration fence now implemented through PlatformServiceMixin instead of USE_DEXIE_DB constant diff --git a/doc/platformservicemixin-completion-plan.md b/doc/platformservicemixin-completion-plan.md index 9eaa7061..b2dfa913 100644 --- a/doc/platformservicemixin-completion-plan.md +++ b/doc/platformservicemixin-completion-plan.md @@ -7,6 +7,7 @@ This document outlines the complete plan to finish PlatformServiceMixin implemen ## Current Status ### ✅ **PlatformServiceMixin - 95% Complete** + - **Core functionality**: ✅ Implemented - **Caching system**: ✅ Implemented - **Database methods**: ✅ Implemented @@ -14,6 +15,7 @@ This document outlines the complete plan to finish PlatformServiceMixin implemen - **Type definitions**: ✅ Implemented ### ⚠️ **Remaining Issues** + 1. **Single circular dependency**: `memoryLogs` import from databaseUtil 2. **Missing utility functions**: `generateInsertStatement`, `generateUpdateStatement` 3. **52 files** still importing databaseUtil @@ -25,6 +27,7 @@ This document outlines the complete plan to finish PlatformServiceMixin implemen ### **Phase 1: Remove Circular Dependency (30 minutes)** #### **Step 1.1: Create Self-Contained memoryLogs** + ```typescript // In PlatformServiceMixin.ts - Replace line 50: // Remove: import { memoryLogs } from "@/db/databaseUtil"; @@ -48,6 +51,7 @@ $appendToMemoryLogs(message: string): void { ``` #### **Step 1.2: Update logger.ts** + ```typescript // In logger.ts - Replace memoryLogs usage: // Remove: import { memoryLogs } from "@/db/databaseUtil"; @@ -70,6 +74,7 @@ export function getMemoryLogs(): string[] { ### **Phase 2: Add Missing Utility Functions (1 hour)** #### **Step 2.1: Add generateInsertStatement to PlatformServiceMixin** + ```typescript // Add to PlatformServiceMixin methods: _generateInsertStatement( @@ -95,6 +100,7 @@ _generateInsertStatement( ``` #### **Step 2.2: Add generateUpdateStatement to PlatformServiceMixin** + ```typescript // Add to PlatformServiceMixin methods: _generateUpdateStatement( @@ -129,6 +135,7 @@ _generateUpdateStatement( ``` #### **Step 2.3: Add Public Wrapper Methods** + ```typescript // Add to PlatformServiceMixin methods: $generateInsertStatement( @@ -151,6 +158,7 @@ $generateUpdateStatement( ### **Phase 3: Update Type Definitions (30 minutes)** #### **Step 3.1: Update IPlatformServiceMixin Interface** + ```typescript // Add to IPlatformServiceMixin interface: $generateInsertStatement( @@ -167,6 +175,7 @@ $appendToMemoryLogs(message: string): void; ``` #### **Step 3.2: Update ComponentCustomProperties** + ```typescript // Add to ComponentCustomProperties interface: $generateInsertStatement( @@ -185,12 +194,14 @@ $appendToMemoryLogs(message: string): void; ### **Phase 4: Test PlatformServiceMixin (1 hour)** #### **Step 4.1: Create Test Component** + ```typescript // Create test file: src/test/PlatformServiceMixin.test.ts // Test all methods including new utility functions ``` #### **Step 4.2: Run Linting and Type Checking** + ```bash npm run lint npx tsc --noEmit @@ -203,6 +214,7 @@ npx tsc --noEmit ### **Migration Strategy** #### **Priority Order:** + 1. **Views** (25 files) - User-facing components 2. **Components** (15 files) - Reusable UI components 3. **Services** (8 files) - Business logic @@ -211,6 +223,7 @@ npx tsc --noEmit #### **Migration Pattern for Each File:** **Step 1: Add PlatformServiceMixin** + ```typescript // Add to component imports: import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; @@ -223,6 +236,7 @@ export default class ComponentName extends Vue { ``` **Step 2: Replace databaseUtil Imports** + ```typescript // Remove: import { @@ -244,6 +258,7 @@ import { ``` **Step 3: Update Method Calls** + ```typescript // Before: const { sql, params } = generateInsertStatement(contact, 'contacts'); @@ -255,6 +270,7 @@ const { sql, params } = this.$generateInsertStatement(contact, 'contacts'); ### **File Migration Checklist** #### **Views (25 files) - Priority 1** + - [ ] QuickActionBvcEndView.vue - [ ] ProjectsView.vue - [ ] ClaimReportCertificateView.vue @@ -278,6 +294,7 @@ const { sql, params } = this.$generateInsertStatement(contact, 'contacts'); - [ ] [5 more view files] #### **Components (15 files) - Priority 2** + - [ ] ActivityListItem.vue - [ ] AmountInput.vue - [ ] ChoiceButtonDialog.vue @@ -295,18 +312,21 @@ const { sql, params } = this.$generateInsertStatement(contact, 'contacts'); - [ ] IconRenderer.vue #### **Services (8 files) - Priority 3** + - [ ] api.ts - [ ] endorserServer.ts - [ ] partnerServer.ts - [ ] [5 more service files] #### **Utils (4 files) - Priority 4** + - [ ] LogCollector.ts - [ ] [3 more util files] ### **Migration Tools** #### **Automated Script for Common Patterns** + ```bash #!/bin/bash # migration-helper.sh @@ -326,6 +346,7 @@ echo "logConsoleAndDb → this.\$logAndConsole" ``` #### **Validation Script** + ```bash #!/bin/bash # validate-migration.sh @@ -350,6 +371,7 @@ echo "Migration validation complete!" ## 🎯 **Success Criteria** ### **Day 1 Success Criteria:** + - [ ] PlatformServiceMixin has no circular dependencies - [ ] All utility functions implemented and tested - [ ] Type definitions complete and accurate @@ -357,6 +379,7 @@ echo "Migration validation complete!" - [ ] TypeScript compilation passes ### **Day 2 Success Criteria:** + - [ ] 0 files importing databaseUtil - [ ] All 52 files migrated to PlatformServiceMixin - [ ] No runtime errors in migrated components @@ -364,6 +387,7 @@ echo "Migration validation complete!" - [ ] Performance maintained or improved ### **Overall Success Criteria:** + - [ ] Complete elimination of databaseUtil dependency - [ ] PlatformServiceMixin is the single source of truth for database operations - [ ] Migration fence is fully implemented @@ -386,12 +410,14 @@ echo "Migration validation complete!" ## 📋 **Daily Progress Tracking** ### **Day 1 Progress:** + - [ ] Phase 1: Circular dependency resolved - [ ] Phase 2: Utility functions added - [ ] Phase 3: Type definitions updated - [ ] Phase 4: Testing completed ### **Day 2 Progress:** + - [ ] Views migrated (0/25) - [ ] Components migrated (0/15) - [ ] Services migrated (0/8) @@ -403,16 +429,19 @@ echo "Migration validation complete!" ## 🆘 **Contingency Plans** ### **If Day 1 Takes Longer:** + - Focus on core functionality first - Defer advanced utility functions to Day 2 - Prioritize circular dependency resolution ### **If Day 2 Takes Longer:** + - Focus on high-impact views first - Batch similar components together - Use automated scripts for common patterns ### **If Issues Arise:** + - Document specific problems - Create targeted fixes -- Maintain backward compatibility during transition \ No newline at end of file +- Maintain backward compatibility during transition diff --git a/doc/qr-code-implementation-guide.md b/doc/qr-code-implementation-guide.md index 13405160..2ebe5025 100644 --- a/doc/qr-code-implementation-guide.md +++ b/doc/qr-code-implementation-guide.md @@ -7,6 +7,7 @@ This document describes the QR code scanning and generation implementation in th ## Architecture ### Directory Structure + ``` src/ ├── services/ @@ -74,6 +75,7 @@ interface QRScannerOptions { ### Platform-Specific Implementations #### Mobile (Capacitor) + - Uses `@capacitor-mlkit/barcode-scanning` - Native camera access through platform APIs - Optimized for mobile performance @@ -82,6 +84,7 @@ interface QRScannerOptions { - Back camera preferred for scanning Configuration: + ```typescript // capacitor.config.ts const config: CapacitorConfig = { @@ -105,6 +108,7 @@ const config: CapacitorConfig = { ``` #### Web + - Uses browser's MediaDevices API - Vue.js components for UI - EventEmitter for stream management @@ -116,6 +120,7 @@ const config: CapacitorConfig = { ### View Components #### ContactQRScanView + - Dedicated view for scanning QR codes - Full-screen camera interface - Simple UI focused on scanning @@ -123,6 +128,7 @@ const config: CapacitorConfig = { - Streamlined scanning experience #### ContactQRScanShowView + - Combined view for QR code display and scanning - Shows user's own QR code - Handles user registration status @@ -160,6 +166,7 @@ const config: CapacitorConfig = { ## Build Configuration ### Common Vite Configuration + ```typescript // vite.config.common.mts export async function createBuildConfig(mode: string) { @@ -183,6 +190,7 @@ export async function createBuildConfig(mode: string) { ``` ### Platform-Specific Builds + ```json { "scripts": { @@ -196,6 +204,7 @@ export async function createBuildConfig(mode: string) { ## Error Handling ### Common Error Scenarios + 1. No camera found 2. Permission denied 3. Camera in use by another application @@ -207,6 +216,7 @@ export async function createBuildConfig(mode: string) { 9. Network connectivity issues ### Error Response + - User-friendly error messages - Troubleshooting tips - Clear instructions for resolution @@ -215,6 +225,7 @@ export async function createBuildConfig(mode: string) { ## Security Considerations ### QR Code Security + - Encryption of contact data - Timestamp validation - Version checking @@ -222,6 +233,7 @@ export async function createBuildConfig(mode: string) { - Rate limiting for scans ### Data Protection + - Secure transmission of contact data - Validation of QR code authenticity - Prevention of duplicate scans @@ -231,6 +243,7 @@ export async function createBuildConfig(mode: string) { ## Best Practices ### Camera Access + 1. Always check for camera availability 2. Request permissions explicitly 3. Handle all error conditions @@ -238,6 +251,7 @@ export async function createBuildConfig(mode: string) { 5. Implement proper cleanup ### Performance + 1. Optimize camera resolution 2. Implement proper resource cleanup 3. Handle camera switching efficiently @@ -245,6 +259,7 @@ export async function createBuildConfig(mode: string) { 5. Battery usage optimization ### User Experience + 1. Clear visual feedback 2. Camera preview 3. Scanning status indicators @@ -257,6 +272,7 @@ export async function createBuildConfig(mode: string) { ## Testing ### Test Scenarios + 1. Permission handling 2. Camera switching 3. Error conditions @@ -267,6 +283,7 @@ export async function createBuildConfig(mode: string) { 8. Security validation ### Test Environment + - Multiple browsers - iOS and Android devices - Various network conditions @@ -275,6 +292,7 @@ export async function createBuildConfig(mode: string) { ## Dependencies ### Key Packages + - `@capacitor-mlkit/barcode-scanning` - `qrcode-stream` - `vue-qrcode-reader` @@ -283,12 +301,14 @@ export async function createBuildConfig(mode: string) { ## Maintenance ### Regular Updates + - Keep dependencies updated - Monitor platform changes - Update documentation - Review security patches ### Performance Monitoring + - Track memory usage - Monitor camera performance - Check error rates @@ -436,6 +456,7 @@ The camera switching implementation includes comprehensive error handling: - Camera switch timeout 2. **Error Response** + ```typescript private async handleCameraSwitch(deviceId: string): Promise<void> { try { @@ -460,6 +481,7 @@ The camera switching implementation includes comprehensive error handling: The camera system maintains several states: 1. **Camera States** + ```typescript type CameraState = | "initializing" // Camera is being initialized @@ -529,6 +551,7 @@ The camera system maintains several states: #### MLKit Barcode Scanner Configuration 1. **Plugin Setup** + ```typescript // capacitor.config.ts const config: CapacitorConfig = { @@ -552,6 +575,7 @@ The camera system maintains several states: ``` 2. **Camera Management** + ```typescript // CapacitorQRScanner.ts export class CapacitorQRScanner implements QRScannerService { @@ -603,6 +627,7 @@ The camera system maintains several states: ``` 3. **Camera State Management** + ```typescript // CapacitorQRScanner.ts private async handleCameraState(): Promise<void> { @@ -645,6 +670,7 @@ The camera system maintains several states: ``` 4. **Error Handling** + ```typescript // CapacitorQRScanner.ts private async handleCameraError(error: Error): Promise<void> { @@ -737,6 +763,7 @@ The camera system maintains several states: #### Performance Optimization 1. **Battery Usage** + ```typescript // CapacitorQRScanner.ts private optimizeBatteryUsage(): void { @@ -759,6 +786,7 @@ The camera system maintains several states: ``` 2. **Memory Management** + ```typescript // CapacitorQRScanner.ts private async cleanupResources(): Promise<void> { @@ -802,4 +830,4 @@ The camera system maintains several states: - Camera switching speed - QR code detection speed - App responsiveness - - Background/foreground transitions \ No newline at end of file + - Background/foreground transitions diff --git a/doc/secure-storage-implementation.md b/doc/secure-storage-implementation.md index ba4f04da..012d83b0 100644 --- a/doc/secure-storage-implementation.md +++ b/doc/secure-storage-implementation.md @@ -111,6 +111,7 @@ export class AbsurdSqlDatabaseService implements PlatformService { ``` Key features: + - Uses absurd-sql for SQLite in the browser - Implements operation queuing for thread safety - Handles initialization and connection management @@ -143,6 +144,7 @@ async function getAccount(did: string): Promise<Account | undefined> { When converting from Dexie.js to SQL-based implementation, follow these patterns: 1. **Database Access Pattern** + ```typescript // Before (Dexie) const result = await db.table.where("field").equals(value).first(); @@ -161,6 +163,7 @@ When converting from Dexie.js to SQL-based implementation, follow these patterns ``` 2. **Update Operations** + ```typescript // Before (Dexie) await db.table.where("id").equals(id).modify(changes); @@ -184,6 +187,7 @@ When converting from Dexie.js to SQL-based implementation, follow these patterns ``` 3. **Insert Operations** + ```typescript // Before (Dexie) await db.table.add(item); @@ -202,6 +206,7 @@ When converting from Dexie.js to SQL-based implementation, follow these patterns ``` 4. **Delete Operations** + ```typescript // Before (Dexie) await db.table.where("id").equals(id).delete(); @@ -216,6 +221,7 @@ When converting from Dexie.js to SQL-based implementation, follow these patterns ``` 5. **Result Processing** + ```typescript // Before (Dexie) const items = await db.table.toArray(); @@ -247,6 +253,7 @@ await databaseUtil.logConsoleAndDb(message, showInConsole); ``` Key Considerations: + - Always use `databaseUtil.mapQueryResultToValues()` to process SQL query results - Use utility methods from `db/index.ts` when available instead of direct SQL - Keep Dexie fallbacks wrapped in migration period checks @@ -254,6 +261,7 @@ Key Considerations: - For updates/inserts/deletes, execute both SQL and Dexie operations during migration period Example Migration: + ```typescript // Before (Dexie) export async function updateSettings(settings: Settings): Promise<void> { @@ -274,6 +282,7 @@ export async function updateSettings(settings: Settings): Promise<void> { ``` Remember to: + - Create database access code to use the platform service, putting it in front of the Dexie version - Instead of removing Dexie-specific code, keep it. @@ -330,4 +339,4 @@ it's during migration then use that result instead of the SQL code's result. 4. **Documentation** - Add API documentation - Create migration guides - - Document security measures \ No newline at end of file + - Document security measures diff --git a/doc/sharebufferarray_spectre_security.md b/doc/sharebufferarray_spectre_security.md index 44faebbb..14cd5af8 100644 --- a/doc/sharebufferarray_spectre_security.md +++ b/doc/sharebufferarray_spectre_security.md @@ -4,11 +4,13 @@ ## 1. Introduction to SharedArrayBuffer ### Overview + - `SharedArrayBuffer` is a JavaScript object that enables **shared memory** access between the main thread and Web Workers. - Unlike `ArrayBuffer`, the memory is **not copied** between threads—allowing **true parallelism**. - Paired with `Atomics`, it allows low-level memory synchronization (e.g., locks, waits). ### Example Use + ```js const sab = new SharedArrayBuffer(1024); const sharedArray = new Uint8Array(sab); @@ -18,6 +20,7 @@ sharedArray[0] = 42; ## 2. Browser Security Requirements ### Security Headers Required to Use SharedArrayBuffer + Modern browsers **restrict access** to `SharedArrayBuffer` due to Spectre-class vulnerabilities. The following **HTTP headers must be set** to enable it: @@ -28,23 +31,28 @@ Cross-Origin-Embedder-Policy: require-corp ``` ### HTTPS Requirement + - Must be served over **HTTPS** (except `localhost` for dev). - These headers enforce **cross-origin isolation**. ### Role of CORS + - CORS **alone is not sufficient**. - However, embedded resources (like scripts and iframes) must still include proper CORS headers if they are to be loaded in a cross-origin isolated context. ## 3. Spectre Vulnerability ### What is Spectre? + - A class of **side-channel attacks** exploiting **speculative execution** in CPUs. - Allows an attacker to read arbitrary memory from the same address space. ### Affected Architectures + - Intel, AMD, ARM — essentially **all modern processors**. ### Why It's Still a Concern + - It's a **hardware flaw**, not just a software bug. - Can't be fully fixed in software without performance penalties. - New Spectre **variants** (e.g., v2, RSB, BranchScope) continue to emerge. @@ -52,16 +60,19 @@ Cross-Origin-Embedder-Policy: require-corp ## 4. Mitigations and Current Limitations ### Browser Mitigations + - **Restricted precision** for `performance.now()`. - **Disabled or gated** access to `SharedArrayBuffer`. - **Reduced or removed** fine-grained timers. ### OS/Hardware Mitigations + - **Kernel Page Table Isolation (KPTI)** - **Microcode updates** - **Retpoline** compiler mitigations ### Developer Responsibilities + - Avoid sharing sensitive data across threads unless necessary. - Use **constant-time cryptographic functions**. - Assume timing attacks are **still possible**. @@ -70,10 +81,12 @@ Cross-Origin-Embedder-Policy: require-corp ## 5. Practical Development Notes ### Using SharedArrayBuffer Safely + - Ensure the site is **cross-origin isolated**: - Serve all resources with appropriate **CORS policies** (`Cross-Origin-Resource-Policy`, `Access-Control-Allow-Origin`) - Set the required **COOP/COEP headers** - Validate support using: + ```js if (window.crossOriginIsolated) { // Safe to use SharedArrayBuffer @@ -81,6 +94,7 @@ if (window.crossOriginIsolated) { ``` ### Testing and Fallback + - Provide fallbacks to `ArrayBuffer` if isolation is not available. - Document use cases clearly (e.g., high-performance WebAssembly applications or real-time audio/video processing). diff --git a/doc/storage-implementation-checklist.md b/doc/storage-implementation-checklist.md index dec776ac..175cc8e1 100644 --- a/doc/storage-implementation-checklist.md +++ b/doc/storage-implementation-checklist.md @@ -3,6 +3,7 @@ ## Core Services ### 1. Storage Service Layer + - [x] Create base `PlatformService` interface - [x] Define common methods for all platforms - [x] Add platform-specific method signatures @@ -25,6 +26,7 @@ - [ ] File system access ### 2. Migration Services + - [x] Implement basic migration support - [x] Dual-storage pattern (SQLite + Dexie) - [x] Basic data verification @@ -37,6 +39,7 @@ - [ ] Manual triggers ### 3. Security Layer + - [x] Basic data integrity - [ ] Implement `EncryptionService` (planned) - [ ] Key management @@ -50,14 +53,17 @@ ## Platform-Specific Implementation ### Web Platform + - [x] Setup absurd-sql - [x] Install dependencies + ```json { "@jlongster/sql.js": "^1.8.0", "absurd-sql": "^1.8.0" } ``` + - [x] Configure VFS with IndexedDB backend - [x] Setup worker threads - [x] Implement operation queuing @@ -83,6 +89,7 @@ - [x] Implement atomic operations ### iOS Platform (Planned) + - [ ] Setup SQLCipher - [ ] Install pod dependencies - [ ] Configure encryption @@ -96,6 +103,7 @@ - [ ] Setup app groups ### Android Platform (Planned) + - [ ] Setup SQLCipher - [ ] Add Gradle dependencies - [ ] Configure encryption @@ -109,6 +117,7 @@ - [ ] Setup file provider ### Electron Platform (Planned) + - [ ] Setup Node SQLite - [ ] Install dependencies - [ ] Configure IPC @@ -124,6 +133,7 @@ ## Data Models and Types ### 1. Database Schema + - [x] Define tables ```sql @@ -166,6 +176,7 @@ ### 2. Type Definitions - [x] Create interfaces + ```typescript interface Account { did: string; @@ -197,6 +208,7 @@ ## UI Components ### 1. Migration UI (Planned) + - [ ] Create components - [ ] `MigrationProgress.vue` - [ ] `MigrationError.vue` @@ -204,6 +216,7 @@ - [ ] `MigrationStatus.vue` ### 2. Settings UI (Planned) + - [ ] Update components - [ ] Add storage settings - [ ] Add migration controls @@ -211,6 +224,7 @@ - [ ] Add security settings ### 3. Error Handling UI (Planned) + - [ ] Create components - [ ] `StorageError.vue` - [ ] `QuotaExceeded.vue` @@ -220,6 +234,7 @@ ## Testing ### 1. Unit Tests + - [x] Basic service tests - [x] Platform service tests - [x] Database operation tests @@ -227,6 +242,7 @@ - [ ] Platform detection tests (planned) ### 2. Integration Tests (Planned) + - [ ] Test migrations - [ ] Web platform tests - [ ] iOS platform tests @@ -234,6 +250,7 @@ - [ ] Electron platform tests ### 3. E2E Tests (Planned) + - [ ] Test workflows - [ ] Account management - [ ] Settings management @@ -243,12 +260,14 @@ ## Documentation ### 1. Technical Documentation + - [x] Update architecture docs - [x] Add API documentation - [ ] Create migration guides (planned) - [ ] Document security measures (planned) ### 2. User Documentation (Planned) + - [ ] Update user guides - [ ] Add troubleshooting guides - [ ] Create FAQ @@ -257,12 +276,14 @@ ## Deployment ### 1. Build Process + - [x] Update build scripts - [x] Add platform-specific builds - [ ] Configure CI/CD (planned) - [ ] Setup automated testing (planned) ### 2. Release Process (Planned) + - [ ] Create release checklist - [ ] Add version management - [ ] Setup rollback procedures @@ -271,12 +292,14 @@ ## Monitoring and Analytics (Planned) ### 1. Error Tracking + - [ ] Setup error logging - [ ] Add performance monitoring - [ ] Configure alerts - [ ] Create dashboards ### 2. Usage Analytics + - [ ] Add storage metrics - [ ] Track migration success - [ ] Monitor performance @@ -285,12 +308,14 @@ ## Security Audit (Planned) ### 1. Code Review + - [ ] Review encryption - [ ] Check access controls - [ ] Verify data handling - [ ] Audit dependencies ### 2. Penetration Testing + - [ ] Test data access - [ ] Verify encryption - [ ] Check authentication @@ -299,6 +324,7 @@ ## Success Criteria ### 1. Performance + - [x] Query response time < 100ms - [x] Operation queuing for thread safety - [x] Proper initialization handling @@ -307,6 +333,7 @@ - [ ] Memory usage < 50MB (planned) ### 2. Reliability + - [x] Basic data integrity - [x] Operation queuing - [ ] Automatic recovery (planned) @@ -315,6 +342,7 @@ - [ ] Data consistency (planned) ### 3. Security + - [x] Basic data integrity - [ ] AES-256 encryption (planned) - [ ] Secure key storage (planned) @@ -322,8 +350,9 @@ - [ ] Audit logging (planned) ### 4. User Experience + - [x] Basic database operations - [ ] Smooth migration (planned) - [ ] Clear error messages (planned) - [ ] Progress indicators (planned) -- [ ] Recovery options (planned) \ No newline at end of file +- [ ] Recovery options (planned) diff --git a/doc/usage-guide.md b/doc/usage-guide.md index 214ebf88..596da455 100644 --- a/doc/usage-guide.md +++ b/doc/usage-guide.md @@ -53,10 +53,9 @@ header-includes: \clearpage - # Purpose of Document -Both end-users and development team members need to know how to use TimeSafari. +Both end-users and development team members need to know how to use TimeSafari. This document serves to show how to use every feature of the TimeSafari platform. Sections of this document are geared specifically for software developers and quality assurance @@ -64,7 +63,7 @@ team members. Companion videos will also describe end-to-end workflows for the end-user. -# TimeSafari +# TimeSafari ## Overview @@ -90,49 +89,51 @@ development environment. This section will guide you through the process. ## Prerequisites 1. Have the following installed on your local machine: - - Node.js and NPM - - A web browser. For this guide, we will use Google Chrome. - - Git - - A code editor + +- Node.js and NPM +- A web browser. For this guide, we will use Google Chrome. +- Git +- A code editor 2. Create an API key on Infura. This is necessary for the Endorser API to connect to the Ethereum - blockchain. - - You can create an account on Infura [here](https://infura.io/).\ + blockchain. + +- You can create an account on Infura [here](https://infura.io/).\ Click "CREATE NEW API KEY" and label the key. Then click "API Keys" in the top menu bar to be taken back to the list of keys. - - Click "VIEW STATS" on the key you want to use. - + + Click "VIEW STATS" on the key you want to use. + ![](images/01_infura-api-keys.png){ width=550px } - - Go to the key detail page. Then click "MANAGE API KEY". +- Go to the key detail page. Then click "MANAGE API KEY". ![](images/02-infura-key-detail.png){ width=550px } - - Click the copy and paste button next to the string of alphanumeric characters.\ +- Click the copy and paste button next to the string of alphanumeric characters.\ This is your API, also known as your project ID. ![](images/03-infura-api-key-id.png){width=550px } - - Save this for later during the Endorser API setup. This will go in your `INFURA_PROJECT_ID` +- Save this for later during the Endorser API setup. This will go in your `INFURA_PROJECT_ID` environment variable. - ## Setup steps -### 1. Clone the following repositories from their respective Git hosts: - - [TimeSafari Frontend](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa)\ +### 1. Clone the following repositories from their respective Git hosts + +- [TimeSafari Frontend](https://gitea.anomalistdesign.com/trent_larson/crowd-funder-for-time-pwa)\ This is a Progressive Web App (PWA) built with VueJS and TypeScript. Note that the clone command here is different from the one you would use for GitHub. - + ```bash git clone git clone \ ssh://git@gitea.anomalistdesign.com:222/trent_larson/crowd-funder-for-time-pwa.git ``` - - [TimeSafari Backend - Endorser API](https://github.com/trentlarson/endorser-ch)\ +- [TimeSafari Backend - Endorser API](https://github.com/trentlarson/endorser-ch)\ This is a NodeJS service providing the backend for TimeSafari. - + ```bash git clone git@github.com:trentlarson/endorser-ch.git ``` @@ -148,7 +149,7 @@ below to generate sample data. Then copy the test database, rename it to `-dev` `cp ../endorser-ch-test-local.sqlite3 ../endorser-ch-dev.sqlite3` \ and rerun `npm run dev` to give yourself user #0 and others from the ETHR_CRED_DATA in [the endorser.ch test util file](https://github.com/trentlarson/endorser-ch/blob/master/test/util.js#L90) -#### Alternative 2 - boostrap single seed user +#### Alternative 2 - boostrap single seed user In this method you will end up with two accounts in the database, one for the first boostrap user, and the second as the primary user you will use during testing. The first user will invite the @@ -157,26 +158,30 @@ second user to the app. 1. Install dependencies and environment variables.\ In endorser-ch install dependencies and set up environment variables to allow starting it up in development mode. + ```bash cd endorser-ch npm clean install # or npm ci cp .env.local .env ``` + Edit the .env file's INFURA_PROJECT_ID with the value you saved earlier in the prerequisites.\ Then create the SQLite database by running `npm run flyway migrate` with environment variables set correctly to select the default SQLite development user as follows. + ```bash export NODE_ENV=dev export DBUSER=sa export DBPASS=sasa npm run flyway migrate - ``` - The first run of flyway migrate may take some time to complete because the entire Flyway + ``` + + The first run of flyway migrate may take some time to complete because the entire Flyway distribution must be downloaded prior to executing migrations. - + Successful output looks similar to the following: - + ``` Database: jdbc:sqlite:../endorser-ch-dev.sqlite3 (SQLite 3.41) Schema history table "main"."flyway_schema_history" does not exist yet @@ -202,23 +207,23 @@ A Flyway report has been generated here: /Users/kbull/code/timesafari/endorser-c 2. Generate the first user in TimeSafari PWA and bootstrap that user in Endorser's database.\ As TimeSafari is an invite-only platform the first user must be manually bootstrapped since no other users exist to be able to invite the first user. This first user must be added manually - to the SQLite database used by Endorser. In this setup you generate the first user from the PWA. - - This user is automatically generated on first usage of the TimeSafari PWA. Bootstrapping that + to the SQLite database used by Endorser. In this setup you generate the first user from the PWA. + + This user is automatically generated on first usage of the TimeSafari PWA. Bootstrapping that user is required so that this first user can register other users. - Change directories into `crowd-funder-for-time-pwa` - + ```bash cd .. cd crowd-funder-for-time-pwa ``` - + - Ensure the `.env.development` file exists and has the following values: - + ```env VITE_DEFAULT_ENDORSER_API_SERVER=http://127.0.0.1:3000 ``` - + - Install dependencies and run in dev mode. For now don't worry about configuring the app. All we need is to generate the first root user and this happens automatically on app startup. @@ -230,45 +235,45 @@ A Flyway report has been generated here: /Users/kbull/code/timesafari/endorser-c - Open the app in a browser and go to the developer tools. It is recommended to use a completely separate browser profile so you do not clear out your existing user account. We will be completely resetting the PWA app state prior to generating the first user. - + In the Developer Tools go to the Application tab. - - ![](images/04-pwa-chrome-devtools.png){width=350px} - + + ![](images/04-pwa-chrome-devtools.png){width=350px} + Click the "Clear site data" button and then refresh the page. - + - Click the account button in the bottom right corner of the page. - + ![](images/05-pwa-account-button.png){width=150px} - + - This will take you to the account page titled "Your Identity" on which you can see your DID, a `did:ethr` DID in this case. - + ![](images/06-pwa-account-page.png){width=350px} - + - Copy the DID by selecting it and copying it to the clipboard or by clicking the copy and paste button as shown in the image. - + ![](images/07-pwa-did-copied.png){width=200px} - + In our case this DID is:\ `did:ethr:0xe4B783c74c8B0e229524e44d0cD898D272E02CD6` - - - Add that DID to the following echoed SQL statement where it says `YOUR_DID` + +- Add that DID to the following echoed SQL statement where it says `YOUR_DID` ```bash echo "INSERT INTO registration (did, maxClaims, maxRegs, epoch) VALUES ('YOUR_DID', 100, 10000, 1719348718092);" | sqlite3 ./endorser-ch-dev.sqlite3 ``` - + and run this command in the parent directory just above the `endorser-ch` directory. - - It needs to be the parent directory of your `endorser-ch` repository because when + + It needs to be the parent directory of your `endorser-ch` repository because when `endorser-ch` creates the SQLite database it depends on it creates it in the parent directory of `endorser-ch`. - - - You can verify with an SQL browser tool that your record has been added to the `registration` + +- You can verify with an SQL browser tool that your record has been added to the `registration` table. ![](images/08-endorser-sqlite-row-added.png){width=350px} @@ -285,14 +290,14 @@ A Flyway report has been generated here: /Users/kbull/code/timesafari/endorser-c 4. Create the second user by opening up a separate browser profile or incognito session, opening the TimeSafari PWA at `http://localhost:8080`. You will see the yellow banner stating "Someone must register you before you can give or offer." - + ![](images/09-pwa-second-profile-first-open.png){width=350px} - + - If you want to ensure you have a fresh user account then open the developer tools, clear the - Application data as before, and then refresh the page. This will generate a new user in the + Application data as before, and then refresh the page. This will generate a new user in the browser's IndexedDB database. 5. Go to the second users' account page to copy the DID. - + ![](images/10-pwa-second-user-did.png){width=350px} 6. Copy the DID and put it in the text bar on the "Your Contacts" page for the first account diff --git a/doc/z-index-guide.md b/doc/z-index-guide.md new file mode 100644 index 00000000..49a5733a --- /dev/null +++ b/doc/z-index-guide.md @@ -0,0 +1,69 @@ +# Z-Index Guide — TimeSafari + +**Author**: Development Team +**Date**: 2025-08-25T19:38:09-08:00 +**Status**: 🎯 **ACTIVE** - Z-index layering standards + +## Objective +Establish consistent z-index values across the TimeSafari application to ensure proper layering of UI elements. + +## Result +This document defines the z-index hierarchy for all UI components. + +## Use/Run +Reference these values when implementing new components or modifying existing ones to maintain consistent layering. + +## Z-Index Hierarchy + +| Component | Z-Index | Usage | +|-----------|---------|-------| +| **Map** | `40` | Base map layer and map-related overlays | +| **QuickNav** | `50` | Quick navigation bottom bar | +| **Dialogs and Modals** | `100` | Modal dialogs, popups, and overlay content | +| **Notifications and Toasts** | `120` | System notifications, alerts, and toast messages | + +## Best Practices + +1. **Never exceed 120** - Keep the highest z-index reserved for critical notifications +2. **Use increments of 10** - Leave room for future additions between layers +3. **Document exceptions** - If you need a z-index outside this range, document the reason +4. **Test layering** - Verify z-index behavior across different screen sizes and devices + +## Common Pitfalls + +- **Avoid arbitrary values** - Don't use random z-index numbers +- **Don't nest high z-index** - Keep child elements within their parent's z-index range +- **Consider stacking context** - Remember that `position: relative` creates new stacking contexts + +## Next Steps + +| Owner | Task | Exit Criteria | Target Date | +|-------|------|---------------|-------------| +| Dev Team | Apply z-index classes to existing components | All components use defined z-index values | 2025-09-01 | + +## Competence Hooks + +- **Why this works**: Creates predictable layering hierarchy that prevents UI conflicts +- **Common pitfalls**: Using arbitrary z-index values or exceeding the defined range +- **Next skill unlock**: Learn about CSS stacking contexts and their impact on z-index +- **Teach-back**: Explain the z-index hierarchy to a team member without referencing this guide + +## Collaboration Hooks + +- **Reviewers**: Frontend team, UI/UX designers +- **Sign-off checklist**: + - [ ] All new components follow z-index guidelines + - [ ] Existing components updated to use defined values + - [ ] Cross-browser testing completed + - [ ] Mobile responsiveness verified + +## Assumptions & Limits + +- Assumes modern browser support for z-index +- Limited to 4 defined layers (expandable if needed) +- Requires team discipline to maintain consistency + +## References + +- [MDN Z-Index Documentation](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index) +- [CSS Stacking Context Guide](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context) diff --git a/docker/README.md b/docker/README.md index b79a8b6d..a4032778 100644 --- a/docker/README.md +++ b/docker/README.md @@ -155,6 +155,7 @@ VITE_PASSKEYS_ENABLED=true ## Build Modes ### Development Mode + - **Target**: `development` - **Features**: Hot reloading, development server - **Port**: 5173 @@ -168,6 +169,7 @@ docker build --target development -t timesafari:dev . ``` ### Staging Mode + - **Target**: `staging` - **Features**: Production build with relaxed caching - **Port**: 8080 (mapped from 80) @@ -181,6 +183,7 @@ docker build --build-arg BUILD_MODE=staging -t timesafari:staging . ``` ### Production Mode + - **Target**: `production` - **Features**: Optimized production build - **Port**: 80 @@ -194,6 +197,7 @@ docker build -t timesafari:latest . ``` ### Custom Mode + - **Target**: Configurable via `BUILD_TARGET` - **Features**: Fully configurable - **Port**: Configurable via `CUSTOM_PORT` @@ -250,6 +254,7 @@ docker-compose up staging ## Security Features ### Built-in Security + - **Non-root user execution**: All containers run as non-root users - **Security headers**: XSS protection, content type options, frame options - **Rate limiting**: API request rate limiting @@ -257,6 +262,7 @@ docker-compose up staging - **Minimal attack surface**: Alpine Linux base images ### Security Headers + - `X-Frame-Options: SAMEORIGIN` - `X-Content-Type-Options: nosniff` - `X-XSS-Protection: 1; mode=block` @@ -266,17 +272,20 @@ docker-compose up staging ## Performance Optimizations ### Caching Strategy + - **Static assets**: 1 year cache with immutable flag (production) - **HTML files**: 1 hour cache (production) / no cache (staging) - **Service worker**: No cache - **Manifest**: 1 day cache (production) / 1 hour cache (staging) ### Compression + - **Gzip compression**: Enabled for text-based files - **Compression level**: 6 (balanced) - **Minimum size**: 1024 bytes ### Nginx Optimizations + - **Sendfile**: Enabled for efficient file serving - **TCP optimizations**: nopush and nodelay enabled - **Keepalive**: 65 second timeout @@ -285,19 +294,23 @@ docker-compose up staging ## Health Checks ### Built-in Health Checks + All services include health checks that: + - Check every 30 seconds - Timeout after 10 seconds - Retry 3 times before marking unhealthy - Start checking after 40 seconds ### Health Check Endpoints + - **Production/Staging**: `http://localhost/health` - **Development**: `http://localhost:5173` ## SSL/HTTPS Setup ### SSL Certificates + For SSL deployment, create an `ssl` directory with certificates: ```bash @@ -308,6 +321,7 @@ cp your-key.pem ssl/ ``` ### SSL Configuration + Use the `production-ssl` service in docker-compose: ```bash @@ -317,10 +331,12 @@ docker-compose up production-ssl ## Monitoring and Logging ### Log Locations + - **Access logs**: `/var/log/nginx/access.log` - **Error logs**: `/var/log/nginx/error.log` ### Log Format + ``` $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" @@ -328,6 +344,7 @@ $status $body_bytes_sent "$http_referer" ``` ### Log Levels + - **Production**: `warn` level - **Staging**: `debug` level - **Development**: Full logging @@ -337,6 +354,7 @@ $status $body_bytes_sent "$http_referer" ### Common Issues #### Build Failures + ```bash # Check build logs docker build -t timesafari:latest . 2>&1 | tee build.log @@ -349,6 +367,7 @@ docker run --rm timesafari:latest npm list --depth=0 ``` #### Container Won't Start + ```bash # Check container logs docker logs <container_id> @@ -361,6 +380,7 @@ netstat -tulpn | grep :80 ``` #### Environment Variables Not Set + ```bash # Check environment in container docker exec <container_id> env | grep VITE_ @@ -373,6 +393,7 @@ cat .env.production ``` #### Performance Issues + ```bash # Check container resources docker stats <container_id> @@ -387,6 +408,7 @@ docker exec <container_id> tail -f /var/log/nginx/access.log ### Debug Commands #### Container Debugging + ```bash # Enter running container docker exec -it <container_id> /bin/sh @@ -399,6 +421,7 @@ docker exec <container_id> ls -la /usr/share/nginx/html ``` #### Network Debugging + ```bash # Check container network docker network inspect bridge @@ -413,6 +436,7 @@ docker exec <container_id> nslookup google.com ## Production Deployment ### Recommended Production Setup + 1. **Use specific version tags**: `timesafari:1.0.0` 2. **Implement health checks**: Already included 3. **Configure proper logging**: Use external log aggregation @@ -420,6 +444,7 @@ docker exec <container_id> nslookup google.com 5. **Use Docker secrets**: For sensitive data ### Production Commands + ```bash # Build with specific version docker build -t timesafari:1.0.0 . @@ -442,6 +467,7 @@ docker run -d --name timesafari -p 80:80 --restart unless-stopped --env-file .en ## Development Workflow ### Local Development + ```bash # Start development environment ./docker/run.sh dev @@ -454,6 +480,7 @@ docker-compose down dev ``` ### Testing Changes + ```bash # Build and test staging ./docker/run.sh staging @@ -463,6 +490,7 @@ docker-compose down dev ``` ### Continuous Integration + ```bash # Build and test in CI docker build -t timesafari:test . @@ -479,6 +507,7 @@ docker rm timesafari-test ## Best Practices ### Security + - Always use non-root users - Keep base images updated - Scan images for vulnerabilities @@ -486,6 +515,7 @@ docker rm timesafari-test - Implement proper access controls ### Performance + - Use multi-stage builds - Optimize layer caching - Minimize image size @@ -493,6 +523,7 @@ docker rm timesafari-test - Implement proper caching ### Monitoring + - Use health checks - Monitor resource usage - Set up log aggregation @@ -500,8 +531,9 @@ docker rm timesafari-test - Use proper error handling ### Maintenance + - Regular security updates - Monitor for vulnerabilities - Keep dependencies updated - Document configuration changes -- Test deployment procedures \ No newline at end of file +- Test deployment procedures diff --git a/electron/README-BUILDING.md b/electron/README-BUILDING.md index baf3bd17..5b6e33be 100644 --- a/electron/README-BUILDING.md +++ b/electron/README-BUILDING.md @@ -18,6 +18,7 @@ This guide covers building and running the TimeSafari Electron application for d ## Quick Start ### Development Mode + ```bash # Start development server npm run build:electron:dev @@ -28,6 +29,7 @@ npm run electron:start ``` ### Production Builds + ```bash # Build for current platform npm run build:electron:prod @@ -48,16 +50,19 @@ npm run build:electron:deb # Linux DEB package The Electron app enforces single instance operation to prevent database conflicts and resource contention: ### Implementation + - Uses Electron's built-in `app.requestSingleInstanceLock()` - Second instances exit immediately with user-friendly message - Existing instance focuses and shows informational dialog ### Behavior + - **First instance**: Starts normally and acquires lock - **Second instance**: Detects existing instance, exits immediately - **User experience**: Clear messaging about single instance requirement ### Benefits + - Prevents database corruption from concurrent access - Avoids resource conflicts - Maintains data integrity @@ -66,6 +71,7 @@ The Electron app enforces single instance operation to prevent database conflict ## Build Configuration ### Environment Modes + ```bash # Development (default) npm run build:electron:dev @@ -78,6 +84,7 @@ npm run build:electron:prod ``` ### Platform-Specific Builds + ```bash # Windows npm run build:electron:windows:dev @@ -96,6 +103,7 @@ npm run build:electron:linux:prod ``` ### Package Types + ```bash # Linux AppImage npm run build:electron:appimage:dev @@ -116,26 +124,31 @@ npm run build:electron:deb:prod ## Platform-Specific Requirements ### Windows + - Windows 10+ (64-bit) - Visual Studio Build Tools (for native modules) ### macOS + - macOS 10.15+ (Catalina) - Xcode Command Line Tools - Code signing certificate (for distribution) ### Linux + - Ubuntu 18.04+ / Debian 10+ / CentOS 7+ - Development headers for native modules ## Database Configuration ### SQLite Integration + - Uses native Node.js SQLite3 for Electron - Database stored in user's app data directory - Automatic migration from IndexedDB (if applicable) ### Single Instance Protection + - File-based locking prevents concurrent database access - Automatic cleanup on app exit - Graceful handling of lock conflicts @@ -143,11 +156,13 @@ npm run build:electron:deb:prod ## Security Features ### Content Security Policy + - Strict CSP in production builds - Development mode allows localhost connections - Automatic configuration based on build mode ### Auto-Updater + - Disabled in development mode - Production builds check for updates automatically - AppImage builds skip update checks @@ -157,6 +172,7 @@ npm run build:electron:deb:prod ### Common Issues #### Build Failures + ```bash # Clean and rebuild npm run clean:electron @@ -164,6 +180,7 @@ npm run build:electron:dev ``` #### Native Module Issues + ```bash # Rebuild native modules cd electron @@ -171,16 +188,19 @@ npm run electron:rebuild ``` #### Single Instance Conflicts + - Ensure no other TimeSafari instances are running - Check for orphaned processes: `ps aux | grep electron` - Restart system if necessary #### Database Issues + - Check app data directory permissions - Verify SQLite database integrity - Clear app data if corrupted ### Debug Mode + ```bash # Enable debug logging DEBUG=* npm run build:electron:dev @@ -203,6 +223,7 @@ electron/ ## Development Workflow 1. **Start Development** + ```bash npm run build:electron:dev ``` @@ -212,11 +233,13 @@ electron/ - Changes auto-reload in development 3. **Test Build** + ```bash npm run build:electron:test ``` 4. **Production Build** + ```bash npm run build:electron:prod ``` @@ -224,16 +247,19 @@ electron/ ## Performance Considerations ### Memory Usage + - Monitor renderer process memory - Implement proper cleanup in components - Use efficient data structures ### Startup Time + - Lazy load non-critical modules - Optimize database initialization - Minimize synchronous operations ### Database Performance + - Use transactions for bulk operations - Implement proper indexing - Monitor query performance @@ -251,16 +277,19 @@ electron/ ## Deployment ### Distribution + - Windows: `.exe` installer - macOS: `.dmg` disk image - Linux: `.AppImage` or `.deb` package ### Code Signing + - Windows: Authenticode certificate - macOS: Developer ID certificate - Linux: GPG signing (optional) ### Auto-Updates + - Configured for production builds - Disabled for development and AppImage - Handles update failures gracefully @@ -269,4 +298,4 @@ electron/ **Last Updated**: 2025-07-11 **Version**: 1.0.3-beta -**Status**: Production Ready \ No newline at end of file +**Status**: Production Ready diff --git a/electron/README.md b/electron/README.md index 6182fa83..89177fea 100644 --- a/electron/README.md +++ b/electron/README.md @@ -56,21 +56,25 @@ npm run build:electron:dmg:prod ``` **Stage 1: Web Build** + - Vite builds web assets with Electron configuration - Environment variables loaded based on build mode - Assets optimized for desktop application **Stage 2: Capacitor Sync** + - Copies web assets to Electron app directory - Syncs Capacitor configuration and plugins - Prepares native module bindings **Stage 3: TypeScript Compile** + - Compiles Electron main process TypeScript - Rebuilds native modules for target platform - Generates production-ready JavaScript **Stage 4: Package Creation** + - Creates platform-specific installers - Generates distribution packages - Signs applications (when configured) @@ -82,6 +86,7 @@ npm run build:electron:dmg:prod **Purpose**: Local development and testing **Command**: `npm run build:electron:dev` **Features**: + - Hot reload enabled - Debug tools available - Development logging @@ -92,6 +97,7 @@ npm run build:electron:dmg:prod **Purpose**: Staging and testing environments **Command**: `npm run build:electron -- --mode test` **Features**: + - Test API endpoints - Staging configurations - Optimized for testing @@ -102,6 +108,7 @@ npm run build:electron:dmg:prod **Purpose**: Production deployment **Command**: `npm run build:electron -- --mode production` **Features**: + - Production optimizations - Code minification - Security hardening @@ -116,6 +123,7 @@ npm run build:electron:dmg:prod **Command**: `npm run build:electron:windows:prod` **Features**: + - NSIS installer with custom options - Desktop and Start Menu shortcuts - Elevation permissions for installation @@ -128,6 +136,7 @@ npm run build:electron:dmg:prod **Command**: `npm run build:electron:mac:prod` **Features**: + - Universal binary (x64 + arm64) - DMG installer with custom branding - App Store compliance (when configured) @@ -140,6 +149,7 @@ npm run build:electron:dmg:prod **Command**: `npm run build:electron:linux:prod` **Features**: + - AppImage for universal distribution - DEB package for Debian-based systems - RPM package for Red Hat-based systems @@ -152,6 +162,7 @@ npm run build:electron:dmg:prod **Format**: Self-contained Linux executable **Command**: `npm run build:electron:appimage:prod` **Features**: + - Single file distribution - No installation required - Portable across Linux distributions @@ -162,6 +173,7 @@ npm run build:electron:dmg:prod **Format**: Debian package installer **Command**: `npm run build:electron:deb:prod` **Features**: + - Native package management - Dependency resolution - System integration @@ -172,6 +184,7 @@ npm run build:electron:dmg:prod **Format**: macOS disk image **Command**: `npm run build:electron:dmg:prod` **Features**: + - Native macOS installer - Custom branding and layout - Drag-and-drop installation @@ -293,6 +306,7 @@ Local Electron scripts for building: ### Environment Variables **Development**: + ```bash VITE_API_URL=http://localhost:3000 VITE_DEBUG=true @@ -301,6 +315,7 @@ VITE_ENABLE_DEV_TOOLS=true ``` **Testing**: + ```bash VITE_API_URL=https://test-api.timesafari.com VITE_DEBUG=false @@ -309,6 +324,7 @@ VITE_ENABLE_DEV_TOOLS=false ``` **Production**: + ```bash VITE_API_URL=https://api.timesafari.com VITE_DEBUG=false @@ -347,6 +363,7 @@ electron/ ### Common Issues **TypeScript Compilation Errors**: + ```bash # Clean and rebuild npm run clean:electron @@ -354,18 +371,21 @@ cd electron && npm run build ``` **Native Module Issues**: + ```bash # Rebuild native modules cd electron && npm run build ``` **Asset Copy Issues**: + ```bash # Verify Capacitor sync npx cap sync electron ``` **Package Creation Failures**: + ```bash # Check electron-builder configuration # Verify platform-specific requirements @@ -375,16 +395,19 @@ npx cap sync electron ### Platform-Specific Issues **Windows**: + - Ensure Windows Build Tools installed - Check NSIS installation - Verify code signing certificates **macOS**: + - Install Xcode Command Line Tools - Configure code signing certificates - Check app notarization requirements **Linux**: + - Install required packages (rpm-tools, etc.) - Check AppImage dependencies - Verify desktop integration @@ -394,11 +417,13 @@ npx cap sync electron ### Build Performance **Parallel Builds**: + - Use concurrent TypeScript compilation - Optimize asset copying - Minimize file system operations **Caching Strategies**: + - Cache node_modules between builds - Cache compiled TypeScript - Cache web assets when unchanged @@ -406,11 +431,13 @@ npx cap sync electron ### Runtime Performance **Application Startup**: + - Optimize main process initialization - Minimize startup dependencies - Use lazy loading for features **Memory Management**: + - Monitor memory usage - Implement proper cleanup - Optimize asset loading @@ -420,16 +447,19 @@ npx cap sync electron ### Code Signing **Windows**: + - Authenticode code signing - EV certificate for SmartScreen - Timestamp server configuration **macOS**: + - Developer ID code signing - App notarization - Hardened runtime **Linux**: + - GPG signing for packages - AppImage signing - Package verification @@ -437,12 +467,14 @@ npx cap sync electron ### Security Hardening **Production Builds**: + - Disable developer tools - Remove debug information - Enable security policies - Implement sandboxing **Update Security**: + - Secure update channels - Package integrity verification - Rollback capabilities @@ -496,4 +528,4 @@ npx cap sync electron **Status**: Production ready **Last Updated**: 2025-01-27 **Version**: 1.0 -**Maintainer**: Matthew Raymer \ No newline at end of file +**Maintainer**: Matthew Raymer diff --git a/electron/capacitor.config.ts b/electron/capacitor.config.ts new file mode 100644 index 00000000..24ef38c6 --- /dev/null +++ b/electron/capacitor.config.ts @@ -0,0 +1,116 @@ +import { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'app.timesafari', + appName: 'TimeSafari', + webDir: 'dist', + server: { + cleartext: true + }, + plugins: { + App: { + appUrlOpen: { + handlers: [ + { + url: 'timesafari://*', + autoVerify: true + } + ] + } + }, + SplashScreen: { + launchShowDuration: 3000, + launchAutoHide: true, + backgroundColor: '#ffffff', + androidSplashResourceName: 'splash', + androidScaleType: 'CENTER_CROP', + showSpinner: false, + androidSpinnerStyle: 'large', + iosSpinnerStyle: 'small', + spinnerColor: '#999999', + splashFullScreen: true, + splashImmersive: true + }, + CapSQLite: { + iosDatabaseLocation: 'Library/CapacitorDatabase', + iosIsEncryption: false, + iosBiometric: { + biometricAuth: false, + biometricTitle: 'Biometric login for TimeSafari' + }, + androidIsEncryption: false, + androidBiometric: { + biometricAuth: false, + biometricTitle: 'Biometric login for TimeSafari' + }, + electronIsEncryption: false + } + }, + ios: { + contentInset: 'never', + allowsLinkPreview: true, + scrollEnabled: true, + limitsNavigationsToAppBoundDomains: true, + backgroundColor: '#ffffff', + allowNavigation: [ + '*.timesafari.app', + '*.jsdelivr.net', + 'api.endorser.ch' + ] + }, + android: { + allowMixedContent: true, + captureInput: true, + webContentsDebuggingEnabled: false, + allowNavigation: [ + '*.timesafari.app', + '*.jsdelivr.net', + 'api.endorser.ch', + '10.0.2.2:3000' + ] + }, + electron: { + deepLinking: { + schemes: ['timesafari'] + }, + buildOptions: { + appId: 'app.timesafari', + productName: 'TimeSafari', + directories: { + output: 'dist-electron-packages' + }, + files: [ + 'dist/**/*', + 'electron/**/*' + ], + mac: { + category: 'public.app-category.productivity', + target: [ + { + target: 'dmg', + arch: ['x64', 'arm64'] + } + ] + }, + win: { + target: [ + { + target: 'nsis', + arch: ['x64'] + } + ] + }, + linux: { + target: [ + { + target: 'AppImage', + arch: ['x64'] + } + ], + category: 'Utility' + } + } + } +}; + +export default config; diff --git a/electron/package-lock.json b/electron/package-lock.json index 98a7fbdd..9cf915f4 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -56,7 +56,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/@capacitor-community/sqlite/-/sqlite-6.0.2.tgz", "integrity": "sha512-sj+2SPLu7E/3dM3xxcWwfNomG+aQHuN96/EFGrOtp4Dv30/2y5oIPyi6hZGjQGjPc5GDNoTQwW7vxWNzybjuMg==", - "license": "MIT", "dependencies": { "jeep-sqlite": "^2.7.2" }, diff --git a/electron/src/index.ts b/electron/src/index.ts index 3ca3215e..a7712f3d 100644 --- a/electron/src/index.ts +++ b/electron/src/index.ts @@ -50,6 +50,7 @@ process.stderr.on('error', (err) => { const trayMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [new MenuItem({ label: 'Quit App', role: 'quit' })]; const appMenuBarMenuTemplate: (MenuItemConstructorOptions | MenuItem)[] = [ { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, + { role: 'editMenu' }, { role: 'viewMenu' }, ]; diff --git a/electron/src/setup.ts b/electron/src/setup.ts index 55d79f1a..19c2673d 100644 --- a/electron/src/setup.ts +++ b/electron/src/setup.ts @@ -53,6 +53,7 @@ export class ElectronCapacitorApp { ]; private AppMenuBarMenuTemplate: (MenuItem | MenuItemConstructorOptions)[] = [ { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, + { role: 'editMenu' }, { role: 'viewMenu' }, ]; private mainWindowState; diff --git a/electron/tsconfig.json b/electron/tsconfig.json index b590aebb..d6057ede 100644 --- a/electron/tsconfig.json +++ b/electron/tsconfig.json @@ -1,6 +1,6 @@ { "compileOnSave": true, - "include": ["./src/**/*", "./capacitor.config.ts", "./capacitor.config.js"], + "include": ["./src/**/*"], "compilerOptions": { "outDir": "./build", "importHelpers": true, diff --git a/index.html b/index.html index d2ce14a5..d276108b 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,6 @@ </head> <body> <div id="app"></div> - <script type="module" src="/src/main.web.ts"></script> + <script type="module" src="/src/main.ts"></script> </body> </html> diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj index 381f4bab..5b57160c 100644 --- a/ios/App/App.xcodeproj/project.pbxproj +++ b/ios/App/App.xcodeproj/project.pbxproj @@ -403,7 +403,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEVELOPMENT_TEAM = GM3FS5JQPH; ENABLE_APP_SANDBOX = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -413,7 +413,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.6; + MARKETING_VERSION = 1.0.7; OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\""; PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -430,7 +430,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEVELOPMENT_TEAM = GM3FS5JQPH; ENABLE_APP_SANDBOX = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -440,7 +440,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.6; + MARKETING_VERSION = 1.0.7; PRODUCT_BUNDLE_IDENTIFIER = app.timesafari; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = ""; diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/Contents.json b/ios/App/App/Assets.xcassets/SplashDark.imageset/Contents.json new file mode 100644 index 00000000..0b268f6f --- /dev/null +++ b/ios/App/App/Assets.xcassets/SplashDark.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "splash@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "splash@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "splash@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@1x.png b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@1x.png new file mode 100644 index 00000000..79fc756e Binary files /dev/null and b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@1x.png differ diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@2x.png b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@2x.png new file mode 100644 index 00000000..03f44f99 Binary files /dev/null and b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@2x.png differ diff --git a/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@3x.png b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@3x.png new file mode 100644 index 00000000..a1dbc775 Binary files /dev/null and b/ios/App/App/Assets.xcassets/SplashDark.imageset/splash@3x.png differ diff --git a/ios/App/Podfile b/ios/App/Podfile index da98dfe6..efae46b4 100644 --- a/ios/App/Podfile +++ b/ios/App/Podfile @@ -15,8 +15,10 @@ def capacitor_pods pod 'CapacitorMlkitBarcodeScanning', :path => '../../node_modules/@capacitor-mlkit/barcode-scanning' pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app' pod 'CapacitorCamera', :path => '../../node_modules/@capacitor/camera' + pod 'CapacitorClipboard', :path => '../../node_modules/@capacitor/clipboard' pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem' pod 'CapacitorShare', :path => '../../node_modules/@capacitor/share' + pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar' pod 'CapawesomeCapacitorFilePicker', :path => '../../node_modules/@capawesome/capacitor-file-picker' end diff --git a/ios/App/Podfile.lock b/ios/App/Podfile.lock index fdd82e86..ab5cb59e 100644 --- a/ios/App/Podfile.lock +++ b/ios/App/Podfile.lock @@ -5,6 +5,8 @@ PODS: - Capacitor - CapacitorCamera (6.1.2): - Capacitor + - CapacitorClipboard (6.0.2): + - Capacitor - CapacitorCommunitySqlite (6.0.2): - Capacitor - SQLCipher @@ -17,6 +19,8 @@ PODS: - GoogleMLKit/BarcodeScanning (= 5.0.0) - CapacitorShare (6.0.3): - Capacitor + - CapacitorStatusBar (6.0.2): + - Capacitor - CapawesomeCapacitorFilePicker (6.2.0): - Capacitor - GoogleDataTransport (9.4.1): @@ -88,11 +92,13 @@ DEPENDENCIES: - "Capacitor (from `../../node_modules/@capacitor/ios`)" - "CapacitorApp (from `../../node_modules/@capacitor/app`)" - "CapacitorCamera (from `../../node_modules/@capacitor/camera`)" + - "CapacitorClipboard (from `../../node_modules/@capacitor/clipboard`)" - "CapacitorCommunitySqlite (from `../../node_modules/@capacitor-community/sqlite`)" - "CapacitorCordova (from `../../node_modules/@capacitor/ios`)" - "CapacitorFilesystem (from `../../node_modules/@capacitor/filesystem`)" - "CapacitorMlkitBarcodeScanning (from `../../node_modules/@capacitor-mlkit/barcode-scanning`)" - "CapacitorShare (from `../../node_modules/@capacitor/share`)" + - "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)" - "CapawesomeCapacitorFilePicker (from `../../node_modules/@capawesome/capacitor-file-picker`)" SPEC REPOS: @@ -119,6 +125,8 @@ EXTERNAL SOURCES: :path: "../../node_modules/@capacitor/app" CapacitorCamera: :path: "../../node_modules/@capacitor/camera" + CapacitorClipboard: + :path: "../../node_modules/@capacitor/clipboard" CapacitorCommunitySqlite: :path: "../../node_modules/@capacitor-community/sqlite" CapacitorCordova: @@ -129,6 +137,8 @@ EXTERNAL SOURCES: :path: "../../node_modules/@capacitor-mlkit/barcode-scanning" CapacitorShare: :path: "../../node_modules/@capacitor/share" + CapacitorStatusBar: + :path: "../../node_modules/@capacitor/status-bar" CapawesomeCapacitorFilePicker: :path: "../../node_modules/@capawesome/capacitor-file-picker" @@ -136,11 +146,13 @@ SPEC CHECKSUMS: Capacitor: c95400d761e376be9da6be5a05f226c0e865cebf CapacitorApp: e1e6b7d05e444d593ca16fd6d76f2b7c48b5aea7 CapacitorCamera: 9bc7b005d0e6f1d5f525b8137045b60cffffce79 + CapacitorClipboard: 4443c3cdb7c77b1533dfe3ff0f9f7756aa8579df CapacitorCommunitySqlite: 0299d20f4b00c2e6aa485a1d8932656753937b9b CapacitorCordova: 8d93e14982f440181be7304aa9559ca631d77fff CapacitorFilesystem: 59270a63c60836248812671aa3b15df673fbaf74 CapacitorMlkitBarcodeScanning: 7652be9c7922f39203a361de735d340ae37e134e CapacitorShare: d2a742baec21c8f3b92b361a2fbd2401cdd8288e + CapacitorStatusBar: b16799a26320ffa52f6c8b01737d5a95bbb8f3eb CapawesomeCapacitorFilePicker: c40822f0a39f86855321943c7829d52bca7f01bd GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a GoogleMLKit: 90ba06e028795a50261f29500d238d6061538711 @@ -157,6 +169,6 @@ SPEC CHECKSUMS: SQLCipher: 31878d8ebd27e5c96db0b7cb695c96e9f8ad77da ZIPFoundation: b8c29ea7ae353b309bc810586181fd073cb3312c -PODFILE CHECKSUM: f987510f7383b04a1b09ea8472bdadcd88b6c924 +PODFILE CHECKSUM: 5fa870b031c7c4e0733e2f96deaf81866c75ff7d COCOAPODS: 1.16.2 diff --git a/ios/App/app_privacy_manifest_fixer/CHANGELOG.md b/ios/App/app_privacy_manifest_fixer/CHANGELOG.md index 0c8a036c..059adbf2 100644 --- a/ios/App/app_privacy_manifest_fixer/CHANGELOG.md +++ b/ios/App/app_privacy_manifest_fixer/CHANGELOG.md @@ -1,30 +1,38 @@ ## 1.4.1 + - Fix macOS app re-signing issue. - Automatically enable Hardened Runtime in macOS codesign. - Add clean script. ## 1.4.0 + - Support for macOS app ([#9](https://github.com/crasowas/app_privacy_manifest_fixer/issues/9)). ## 1.3.11 + - Fix install issue by skipping `PBXAggregateTarget` ([#4](https://github.com/crasowas/app_privacy_manifest_fixer/issues/4)). ## 1.3.10 + - Fix app re-signing issue. - Enhance Build Phases script robustness. ## 1.3.9 + - Add log file output. ## 1.3.8 + - Add version info to privacy access report. - Remove empty tables from privacy access report. ## 1.3.7 + - Enhance API symbols analysis with strings tool. - Improve performance of API usage analysis. ## 1.3.5 + - Fix issue with inaccurate privacy manifest search. - Disable dependency analysis to force the script to run on every build. - Add placeholder for privacy access report. @@ -32,27 +40,34 @@ - Add examples for privacy access report. ## 1.3.0 + - Add privacy access report generation. ## 1.2.3 + - Fix issue with relative path parameter. - Add support for all application targets. ## 1.2.1 + - Fix backup issue with empty user templates directory. ## 1.2.0 + - Add uninstall script. ## 1.1.2 + - Remove `Templates/.gitignore` to track `UserTemplates`. - Fix incorrect use of `App.xcprivacy` template in `App.framework`. ## 1.1.0 + - Add logs for latest release fetch failure. - Fix issue with converting published time to local time. - Disable showing environment variables in the build log. - Add `--install-builds-only` command line option. ## 1.0.0 -- Initial version. \ No newline at end of file + +- Initial version. diff --git a/ios/App/app_privacy_manifest_fixer/README.md b/ios/App/app_privacy_manifest_fixer/README.md index 3296b771..d3ef32b8 100644 --- a/ios/App/app_privacy_manifest_fixer/README.md +++ b/ios/App/app_privacy_manifest_fixer/README.md @@ -150,6 +150,7 @@ The privacy manifest templates are stored in the [`Templates`](https://github.co ### Template Types The templates are categorized as follows: + - **AppTemplate.xcprivacy**: A privacy manifest template for the app. - **FrameworkTemplate.xcprivacy**: A generic privacy manifest template for frameworks. - **FrameworkName.xcprivacy**: A privacy manifest template for a specific framework, available only in the `Templates/UserTemplates` directory. @@ -157,20 +158,24 @@ The templates are categorized as follows: ### Template Priority For an app, the priority of privacy manifest templates is as follows: + - `Templates/UserTemplates/AppTemplate.xcprivacy` > `Templates/AppTemplate.xcprivacy` For a specific framework, the priority of privacy manifest templates is as follows: + - `Templates/UserTemplates/FrameworkName.xcprivacy` > `Templates/UserTemplates/FrameworkTemplate.xcprivacy` > `Templates/FrameworkTemplate.xcprivacy` ### Default Templates The default templates are located in the `Templates` root directory and currently include the following templates: + - `Templates/AppTemplate.xcprivacy` - `Templates/FrameworkTemplate.xcprivacy` These templates will be modified based on the API usage analysis results, especially the `NSPrivacyAccessedAPIType` entries, to generate new privacy manifests for fixes, ensuring compliance with App Store requirements. **If adjustments to the privacy manifest template are needed, such as in the following scenarios, avoid directly modifying the default templates. Instead, use a custom template. If a custom template with the same name exists, it will take precedence over the default template for fixes.** + - Generating a non-compliant privacy manifest due to inaccurate API usage analysis. - Modifying the reason declared in the template. - Adding declarations for collected data. @@ -198,6 +203,7 @@ The privacy access API categories and their associated declared reasons in `Fram ### Custom Templates To create custom templates, place them in the `Templates/UserTemplates` directory with the following structure: + - `Templates/UserTemplates/AppTemplate.xcprivacy` - `Templates/UserTemplates/FrameworkTemplate.xcprivacy` - `Templates/UserTemplates/FrameworkName.xcprivacy` @@ -205,6 +211,7 @@ To create custom templates, place them in the `Templates/UserTemplates` director Among these templates, only `FrameworkTemplate.xcprivacy` will be modified based on the API usage analysis results to adjust the `NSPrivacyAccessedAPIType` entries, thereby generating a new privacy manifest for framework fixes. The other templates will remain unchanged and will be directly used for fixes. **Important Notes:** + - The template for a specific framework must follow the naming convention `FrameworkName.xcprivacy`, where `FrameworkName` should match the name of the framework. For example, the template for `Flutter.framework` should be named `Flutter.xcprivacy`. - For macOS frameworks, the naming convention should be `FrameworkName.Version.xcprivacy`, where the version name is added to distinguish different versions. For a single version macOS framework, the `Version` is typically `A`. - The name of an SDK may not exactly match the name of the framework. To determine the correct framework name, check the `Frameworks` directory in the application bundle after building the project. diff --git a/ios/App/app_privacy_manifest_fixer/README.zh-CN.md b/ios/App/app_privacy_manifest_fixer/README.zh-CN.md index 19bc9a8b..39db4cb3 100644 --- a/ios/App/app_privacy_manifest_fixer/README.zh-CN.md +++ b/ios/App/app_privacy_manifest_fixer/README.zh-CN.md @@ -40,7 +40,7 @@ ```shell sh install.sh <project_path> ``` - + - 如果是 Flutter 项目,`project_path`应为 Flutter 项目中的`ios/macos`目录路径。 - 重复运行安装命令时,工具会先移除现有安装(如果有)。若需修改命令行选项,只需重新运行安装命令,无需先卸载。 @@ -150,6 +150,7 @@ sh clean.sh ### 模板类型 模板分为以下几类: + - **AppTemplate.xcprivacy**:App 的隐私清单模板。 - **FrameworkTemplate.xcprivacy**:通用的 Framework 隐私清单模板。 - **FrameworkName.xcprivacy**:特定的 Framework 隐私清单模板,仅在`Templates/UserTemplates`目录有效。 @@ -157,20 +158,24 @@ sh clean.sh ### 模板优先级 对于 App,隐私清单模板的优先级如下: + - `Templates/UserTemplates/AppTemplate.xcprivacy` > `Templates/AppTemplate.xcprivacy` 对于特定的 Framework,隐私清单模板的优先级如下: + - `Templates/UserTemplates/FrameworkName.xcprivacy` > `Templates/UserTemplates/FrameworkTemplate.xcprivacy` > `Templates/FrameworkTemplate.xcprivacy` ### 默认模板 默认模板位于`Templates`根目录,目前包括以下模板: + - `Templates/AppTemplate.xcprivacy` - `Templates/FrameworkTemplate.xcprivacy` 这些模板将根据 API 使用分析结果进行修改,特别是`NSPrivacyAccessedAPIType`条目将被调整,以生成新的隐私清单用于修复,确保符合 App Store 要求。 **如果需要调整隐私清单模板,例如以下场景,请避免直接修改默认模板,而是使用自定义模板。如果存在相同名称的自定义模板,它将优先于默认模板用于修复。** + - 由于 API 使用分析结果不准确,生成了不合规的隐私清单。 - 需要修改模板中声明的理由。 - 需要声明收集的数据。 @@ -198,6 +203,7 @@ sh clean.sh ### 自定义模板 要创建自定义模板,请将其放在`Templates/UserTemplates`目录,结构如下: + - `Templates/UserTemplates/AppTemplate.xcprivacy` - `Templates/UserTemplates/FrameworkTemplate.xcprivacy` - `Templates/UserTemplates/FrameworkName.xcprivacy` @@ -205,6 +211,7 @@ sh clean.sh 在这些模板中,只有`FrameworkTemplate.xcprivacy`会根据 API 使用分析结果对`NSPrivacyAccessedAPIType`条目进行调整,以生成新的隐私清单用于 Framework 修复。其他模板保持不变,将直接用于修复。 **重要说明:** + - 特定的 Framework 模板必须遵循命名规范`FrameworkName.xcprivacy`,其中`FrameworkName`需与 Framework 的名称匹配。例如`Flutter.framework`的模板应命名为`Flutter.xcprivacy`。 - 对于 macOS Framework,应遵循命名规范`FrameworkName.Version.xcprivacy`,额外增加版本名称用于区分不同的版本。对于单一版本的 macOS Framework,`Version`通常为`A`。 - SDK 的名称可能与 Framework 的名称不完全一致。要确定正确的 Framework 名称,请在构建项目后检查 App 包中的`Frameworks`目录。 @@ -229,7 +236,7 @@ sh Report/report.sh <app_path> <report_output_path> |------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------| | ![Original App Report](https://img.crasowas.dev/app_privacy_manifest_fixer/20241218230746.png) | ![Fixed App Report](https://img.crasowas.dev/app_privacy_manifest_fixer/20241218230822.png) | -## 💡 重要考量 +## 💡 重要考量 - 如果最新版本的 SDK 支持隐私清单,请尽可能升级,以避免不必要的风险。 - 此工具仅为临时解决方案,不应替代正确的 SDK 管理实践。 diff --git a/package-lock.json b/package-lock.json index 814a71ad..693dac6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "timesafari", - "version": "1.0.7-beta", + "version": "1.1.0-beta", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "timesafari", - "version": "1.0.7-beta", + "version": "1.1.0-beta", "dependencies": { "@capacitor-community/electron": "^5.0.1", "@capacitor-community/sqlite": "6.0.2", @@ -15,10 +15,12 @@ "@capacitor/app": "^6.0.0", "@capacitor/camera": "^6.0.0", "@capacitor/cli": "^6.2.0", + "@capacitor/clipboard": "^6.0.2", "@capacitor/core": "^6.2.0", "@capacitor/filesystem": "^6.0.0", "@capacitor/ios": "^6.2.0", "@capacitor/share": "^6.0.3", + "@capacitor/status-bar": "^6.0.2", "@capawesome/capacitor-file-picker": "^6.2.0", "@dicebear/collection": "^5.4.1", "@dicebear/core": "^5.4.1", @@ -85,9 +87,9 @@ "three": "^0.156.1", "ua-parser-js": "^1.0.37", "uint8arrays": "^5.0.0", - "vue": "^3.5.13", + "vue": "3.5.13", "vue-axios": "^3.5.2", - "vue-facing-decorator": "^3.0.4", + "vue-facing-decorator": "3.0.4", "vue-picture-cropper": "^0.7.0", "vue-qrcode-reader": "^5.5.3", "vue-router": "^4.5.0", @@ -96,6 +98,8 @@ }, "devDependencies": { "@capacitor/assets": "^3.0.5", + "@commitlint/cli": "^18.6.1", + "@commitlint/config-conventional": "^18.6.2", "@playwright/test": "^1.54.2", "@types/dom-webcodecs": "^0.1.7", "@types/jest": "^30.0.0", @@ -123,16 +127,21 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-vue": "^9.32.0", "fs-extra": "^11.3.0", + "husky": "^9.0.11", "jest": "^30.0.4", + "lint-staged": "^15.2.2", "markdownlint": "^0.37.4", "markdownlint-cli": "^0.44.0", + "markdownlint-cli2": "^0.18.1", "npm-check-updates": "^17.1.13", "path-browserify": "^1.0.1", "postcss": "^8.4.38", "prettier": "^3.2.5", "rimraf": "^6.0.1", + "serve": "^14.2.4", "tailwindcss": "^3.4.1", "ts-jest": "^29.4.0", + "tsx": "^4.20.4", "typescript": "~5.2.2", "vite": "^5.2.0" } @@ -220,22 +229,22 @@ } }, "node_modules/@babel/core": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", - "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "devOptional": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.6", - "@babel/parser": "^7.28.0", + "@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.0", - "@babel/types": "^7.28.0", + "@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", @@ -261,14 +270,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", - "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@babel/types": "^7.28.0", + "@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" @@ -336,19 +345,19 @@ "license": "ISC" }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.27.1", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", + "@babel/traverse": "^7.28.3", "semver": "^6.3.1" }, "engines": { @@ -484,15 +493,15 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -607,25 +616,25 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz", + "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", - "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", + "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -739,12 +748,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", - "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", + "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.0" + "@babel/types": "^7.28.2" }, "bin": { "parser": "bin/babel-parser.js" @@ -1215,9 +1224,9 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz", - "integrity": "sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.3.tgz", + "integrity": "sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==", "license": "MIT", "optional": true, "peer": true, @@ -1227,7 +1236,7 @@ "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.0" + "@babel/traverse": "^7.28.3" }, "engines": { "node": ">=6.9.0" @@ -1665,9 +1674,9 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.28.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.1.tgz", - "integrity": "sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.3.tgz", + "integrity": "sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==", "license": "MIT", "optional": true, "peer": true, @@ -1682,9 +1691,9 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.0.tgz", - "integrity": "sha512-dGopk9nZrtCs2+nfIem25UuHyt5moSJamArzIoh9/vezUQPmYDOzjaHDCkAzuGJibCIkPup8rMT2+wYB6S73cA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz", + "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==", "license": "MIT", "optional": true, "peer": true, @@ -1849,9 +1858,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", - "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "devOptional": true, "license": "MIT", "engines": { @@ -1874,18 +1883,18 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.2", "debug": "^4.3.1" }, "engines": { @@ -1894,19 +1903,19 @@ }, "node_modules/@babel/traverse--for-generate-function-map": { "name": "@babel/traverse", - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", - "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", + "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", "license": "MIT", "optional": true, "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.0", + "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.0", + "@babel/parser": "^7.28.3", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.0", + "@babel/types": "^7.28.2", "debug": "^4.3.1" }, "engines": { @@ -2295,6 +2304,14 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@capacitor/clipboard": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@capacitor/clipboard/-/clipboard-6.0.2.tgz", + "integrity": "sha512-jQ6UeFra5NP58THNZNb7HtzOZU7cHsjgrbQGVuMTgsK1uTILZpNeh+pfqHbKggba6KaNh5DAsJvEVQGpIR1VBA==", + "peerDependencies": { + "@capacitor/core": "^6.0.0" + } + }, "node_modules/@capacitor/core": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-6.2.1.tgz", @@ -2331,6 +2348,14 @@ "@capacitor/core": "^6.0.0" } }, + "node_modules/@capacitor/status-bar": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-6.0.2.tgz", + "integrity": "sha512-AmRIX6QvFemItlY7/69ARkIAqitRQqJ2qwgZmD1KqgFb78pH+XFXm1guvS/a8CuOOm/IqZ4ddDbl20yxtBqzGA==", + "peerDependencies": { + "@capacitor/core": "^6.0.0" + } + }, "node_modules/@capawesome/capacitor-file-picker": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/@capawesome/capacitor-file-picker/-/capacitor-file-picker-6.2.0.tgz", @@ -2428,323 +2453,533 @@ "win32" ] }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "node_modules/@commitlint/cli": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.6.1.tgz", + "integrity": "sha512-5IDE0a+lWGdkOvKH892HHAZgbAjcj1mT5QrfA/SVbLJV/BbBMGyKN0W5mhgjekPJJwEQdVNvhl9PwUacY58Usw==", + "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@commitlint/format": "^18.6.1", + "@commitlint/lint": "^18.6.1", + "@commitlint/load": "^18.6.1", + "@commitlint/read": "^18.6.1", + "@commitlint/types": "^18.6.1", + "execa": "^5.0.0", + "lodash.isfunction": "^3.0.9", + "resolve-from": "5.0.0", + "resolve-global": "1.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "commitlint": "cli.js" }, "engines": { - "node": ">=12" + "node": ">=v18" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "node_modules/@commitlint/cli/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", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "engines": { + "node": ">=8" } }, - "node_modules/@develar/schema-utils": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", - "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "node_modules/@commitlint/config-conventional": { + "version": "18.6.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.6.3.tgz", + "integrity": "sha512-8ZrRHqF6je+TRaFoJVwszwnOXb/VeYrPmTwPhf0WxpzpGTcYy1p0SPyZ2eRn/sRi/obnWAcobtDAq6+gJQQNhQ==", + "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.0", - "ajv-keywords": "^3.4.1" + "@commitlint/types": "^18.6.1", + "conventional-changelog-conventionalcommits": "^7.0.2" }, "engines": { - "node": ">= 8.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=v18" } }, - "node_modules/@dicebear/adventurer": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/adventurer/-/adventurer-5.4.3.tgz", - "integrity": "sha512-PYv82wc1kwhFeyvZYKUIzOoaSNvRj5yn72yRCTlT3K4qoChKPGIJ7K/GoFoNX3Cj0XAWSkp0shPsGFtpU1o3Kg==", - "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" + "node_modules/@commitlint/config-conventional/node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=16" } }, - "node_modules/@dicebear/adventurer-neutral": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-5.4.3.tgz", - "integrity": "sha512-FdprrUE0zXRlx4UAu5ED+wFkxwNSQSJug+lvNjTQtA675c52kkuct/U9eCcD4vcSAd38v4IIKVBMLP/1tEBVXQ==", + "node_modules/@commitlint/config-validator": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.6.1.tgz", + "integrity": "sha512-05uiToBVfPhepcQWE1ZQBR/Io3+tb3gEotZjnI4tTzzPk16NffN6YABgwFQCLmzZefbDcmwWqJWc2XT47q7Znw==", + "dev": true, "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" + "dependencies": { + "@commitlint/types": "^18.6.1", + "ajv": "^8.11.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/avataaars": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/avataaars/-/avataaars-5.4.3.tgz", - "integrity": "sha512-/zC/IPCI/JOsR6gUmXLo9HH2GFujHSvWfqYf23/n8V4e9Dm0gJ3RtZ/g9Kn+6Rkd8hiNp0BUzjlSEwiONEVOug==", + "node_modules/@commitlint/config-validator/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@dicebear/avataaars-neutral": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/avataaars-neutral/-/avataaars-neutral-5.4.3.tgz", - "integrity": "sha512-3hV80zzqpn2tHaitRqRqokxJ/h5vD4YXwMtdvXJq6AKCVcUiVi9ecHVP3GU4uU2zPSk7VsbadVQMXy7/Z47nRw==", + "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/@commitlint/ensure": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.6.1.tgz", + "integrity": "sha512-BPm6+SspyxQ7ZTsZwXc7TRQL5kh5YWt3euKmEIBZnocMFkJevqs3fbLRb8+8I/cfbVcAo4mxRlpTPfz8zX7SnQ==", + "dev": true, "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" + "dependencies": { + "@commitlint/types": "^18.6.1", + "lodash.camelcase": "^4.3.0", + "lodash.kebabcase": "^4.1.1", + "lodash.snakecase": "^4.1.1", + "lodash.startcase": "^4.4.0", + "lodash.upperfirst": "^4.3.1" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/big-ears": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/big-ears/-/big-ears-5.4.3.tgz", - "integrity": "sha512-MXr0m0JNAGbxDt/onoIeIclOgUZbM1eHzH+i+uyz+9mK10IZPysV5i71cL84ZFDBEEWMKVi3uWfr1sCdck3+eQ==", + "node_modules/@commitlint/execute-rule": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.6.1.tgz", + "integrity": "sha512-7s37a+iWyJiGUeMFF6qBlyZciUkF8odSAnHijbD36YDctLhGKoYltdvuJ/AFfRm6cBLRtRk9cCVPdsEFtt/2rg==", + "dev": true, "license": "MIT", "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "node": ">=v18" } }, - "node_modules/@dicebear/big-ears-neutral": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/big-ears-neutral/-/big-ears-neutral-5.4.3.tgz", - "integrity": "sha512-uMt1MvnQ6pCL0u3rpjeEjryLs5MLLcYkAkidFZ6CpziiQyehkaI/1PwZlv7/zG7CD5wZTmUQKZ5+0U4fWR8rwg==", + "node_modules/@commitlint/format": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.6.1.tgz", + "integrity": "sha512-K8mNcfU/JEFCharj2xVjxGSF+My+FbUHoqR+4GqPGrHNqXOGNio47ziiR4HQUPKtiNs05o8/WyLBoIpMVOP7wg==", + "dev": true, "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" + "dependencies": { + "@commitlint/types": "^18.6.1", + "chalk": "^4.1.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/big-smile": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/big-smile/-/big-smile-5.4.3.tgz", - "integrity": "sha512-JIBi6L9CcEhLE83CpHw/JskF7kmD4t+vIeAU95WNPm3c98lLe7h2k2ocDVnZEKpBUqxKG3lk0rFXoYM5ovSpLw==", + "node_modules/@commitlint/is-ignored": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.6.1.tgz", + "integrity": "sha512-MOfJjkEJj/wOaPBw5jFjTtfnx72RGwqYIROABudOtJKW7isVjFe9j0t8xhceA02QebtYf4P/zea4HIwnXg8rvA==", + "dev": true, "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" + "dependencies": { + "@commitlint/types": "^18.6.1", + "semver": "7.6.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/bottts": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/bottts/-/bottts-5.4.3.tgz", - "integrity": "sha512-yhuIB+8ptZzZgDGYOqNXplELoU/In/5zeiqsHEkJtjAEJZd6BEDqUYMHnB1IgUAic9L3gvPt7t3cv2fPbrFDPQ==", - "license": "MIT", + "node_modules/@commitlint/is-ignored/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=10" + } + }, + "node_modules/@commitlint/is-ignored/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/@dicebear/bottts-neutral": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-5.4.3.tgz", - "integrity": "sha512-92mHaeDDblsF5fxX6f8JIYEGxBdIOhnVPNh88XejHBW4RBSyCvXgGVMvmnh6j4YTRoTQHObTCrxdbll4LG4qtQ==", + "node_modules/@commitlint/lint": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.6.1.tgz", + "integrity": "sha512-8WwIFo3jAuU+h1PkYe5SfnIOzp+TtBHpFr4S8oJWhu44IWKuVx6GOPux3+9H1iHOan/rGBaiacicZkMZuluhfQ==", + "dev": true, "license": "MIT", - "engines": { - "node": "^14.13.1 || >=16.0.0" + "dependencies": { + "@commitlint/is-ignored": "^18.6.1", + "@commitlint/parse": "^18.6.1", + "@commitlint/rules": "^18.6.1", + "@commitlint/types": "^18.6.1" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/collection": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/collection/-/collection-5.4.3.tgz", - "integrity": "sha512-iy+NWb4LL0LtYzxizzpXX7iP1WWFFatlEc8WjAsP6y+eKPXz7DWX0k5uJDEbHmJXpHEpvmaowV8evcL07iHwdw==", + "node_modules/@commitlint/load": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.6.1.tgz", + "integrity": "sha512-p26x8734tSXUHoAw0ERIiHyW4RaI4Bj99D8YgUlVV9SedLf8hlWAfyIFhHRIhfPngLlCe0QYOdRKYFt8gy56TA==", + "dev": true, "license": "MIT", "dependencies": { - "@dicebear/adventurer": "5.4.3", - "@dicebear/adventurer-neutral": "5.4.3", - "@dicebear/avataaars": "5.4.3", - "@dicebear/avataaars-neutral": "5.4.3", - "@dicebear/big-ears": "5.4.3", - "@dicebear/big-ears-neutral": "5.4.3", - "@dicebear/big-smile": "5.4.3", - "@dicebear/bottts": "5.4.3", - "@dicebear/bottts-neutral": "5.4.3", - "@dicebear/croodles": "5.4.3", - "@dicebear/croodles-neutral": "5.4.3", - "@dicebear/fun-emoji": "5.4.3", - "@dicebear/icons": "5.4.3", - "@dicebear/identicon": "5.4.3", - "@dicebear/initials": "5.4.3", - "@dicebear/lorelei": "5.4.3", - "@dicebear/lorelei-neutral": "5.4.3", - "@dicebear/micah": "5.4.3", - "@dicebear/miniavs": "5.4.3", - "@dicebear/open-peeps": "5.4.3", - "@dicebear/personas": "5.4.3", - "@dicebear/pixel-art": "5.4.3", - "@dicebear/pixel-art-neutral": "5.4.3", - "@dicebear/shapes": "5.4.3", - "@dicebear/thumbs": "5.4.3" + "@commitlint/config-validator": "^18.6.1", + "@commitlint/execute-rule": "^18.6.1", + "@commitlint/resolve-extends": "^18.6.1", + "@commitlint/types": "^18.6.1", + "chalk": "^4.1.0", + "cosmiconfig": "^8.3.6", + "cosmiconfig-typescript-loader": "^5.0.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "lodash.uniq": "^4.5.0", + "resolve-from": "^5.0.0" }, "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "node": ">=v18" } }, - "node_modules/@dicebear/converter": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/converter/-/converter-5.4.3.tgz", - "integrity": "sha512-7gXwzNfQf5v7JVOyeuOetP59NxYewOLr6QNZaV/+/Did6B6dUJqvAsHWOhmf/JEWJFDkh/6cyGZEgpM4eSxW4A==", + "node_modules/@commitlint/load/node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "dev": true, "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.7", - "tmp-promise": "^3.0.3" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" }, "peerDependencies": { - "@resvg/resvg-js": "^2.0.0", - "exiftool-vendored": "^16 || ^17 || ^18 || ^19 || ^20 || ^21", - "sharp": "^0.32.6" + "typescript": ">=4.9.5" }, "peerDependenciesMeta": { - "@resvg/resvg-js": { - "optional": true - }, - "exiftool-vendored": { - "optional": true - }, - "sharp": { + "typescript": { "optional": true } } }, - "node_modules/@dicebear/core": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/core/-/core-5.4.3.tgz", - "integrity": "sha512-qOOlqdVD1CWT0VFxABADP62oPF8WosfE+OkREYD+vZ3T7rq8kauHAUbGkvwRoB60uMld7kfZzEJNhjR7QI22IQ==", + "node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.1.0.tgz", + "integrity": "sha512-7PtBB+6FdsOvZyJtlF3hEPpACq7RQX6BVGsgC7/lfVXnKMvNCu/XY3ykreqG5w/rBNdu2z8LCIKoF3kpHHdHlA==", + "dev": true, "license": "MIT", "dependencies": { - "@dicebear/converter": "5.4.3" + "jiti": "^1.21.6" }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=v16" + }, + "peerDependencies": { + "@types/node": "*", + "cosmiconfig": ">=8.2", + "typescript": ">=4" } }, - "node_modules/@dicebear/croodles": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/croodles/-/croodles-5.4.3.tgz", - "integrity": "sha512-eP+BwAb7Pm8msS+hcIMyfleBYSqGngt2Ocu/cNqZV2kSLkfIOrogSb4VJTkVtwbJd2Gpt41d+AxW71QSLN2ARQ==", + "node_modules/@commitlint/load/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": "^14.13.1 || >=16.0.0" + "node": ">=8" + } + }, + "node_modules/@commitlint/message": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.6.1.tgz", + "integrity": "sha512-VKC10UTMLcpVjMIaHHsY1KwhuTQtdIKPkIdVEwWV+YuzKkzhlI3aNy6oo1eAN6b/D2LTtZkJe2enHmX0corYRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=v18" + } + }, + "node_modules/@commitlint/parse": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.6.1.tgz", + "integrity": "sha512-eS/3GREtvVJqGZrwAGRwR9Gdno3YcZ6Xvuaa+vUF8j++wsmxrA2En3n0ccfVO2qVOLJC41ni7jSZhQiJpMPGOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/types": "^18.6.1", + "conventional-changelog-angular": "^7.0.0", + "conventional-commits-parser": "^5.0.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/croodles-neutral": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/croodles-neutral/-/croodles-neutral-5.4.3.tgz", - "integrity": "sha512-Z4wGeeUEdZjer6xKy5jimgDXQtUnW1OKQuV+LjA6bgyLTB/QmtmspZHzqCOgwZFQAydqcUoxEFWmrIpwNLujBA==", + "node_modules/@commitlint/parse/node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@commitlint/parse/node_modules/conventional-commits-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", + "dev": true, "license": "MIT", + "dependencies": { + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.mjs" + }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=16" + } + }, + "node_modules/@commitlint/parse/node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^2.0.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/@dicebear/fun-emoji": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/fun-emoji/-/fun-emoji-5.4.3.tgz", - "integrity": "sha512-La4XonWV0abaIiE4Dj0OOtH7tH5VC1tiXmcapJJ69d3y6YfJnedyhHzD+Xk2gnWFD/X0mnmLJua++BGSrv7YkQ==", + "node_modules/@commitlint/parse/node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "dev": true, "license": "MIT", "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=16.10" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@dicebear/icons": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/icons/-/icons-5.4.3.tgz", - "integrity": "sha512-p173l+PkZBrVtUTgSFVD9dHLc9djkRtxp+tLNsGYkBjeeWmJ/kxtPH5iG7MOp1pUnCHRkWJrLyysV51sgcrNug==", + "node_modules/@commitlint/parse/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/@commitlint/parse/node_modules/text-extensions": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz", + "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==", + "dev": true, "license": "MIT", "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=8" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@dicebear/identicon": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/identicon/-/identicon-5.4.3.tgz", - "integrity": "sha512-it9tHCJ9UWdSwDzbYZVDhx8mZMQKeKXIWzmOWwS/aGHM3aONn8Kjxd6m5rnQGWFVzxxUjedrIOZxFhWuogzYOw==", + "node_modules/@commitlint/read": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.6.1.tgz", + "integrity": "sha512-ia6ODaQFzXrVul07ffSgbZGFajpe8xhnDeLIprLeyfz3ivQU1dIoHp7yz0QIorZ6yuf4nlzg4ZUkluDrGN/J/w==", + "dev": true, "license": "MIT", + "dependencies": { + "@commitlint/top-level": "^18.6.1", + "@commitlint/types": "^18.6.1", + "git-raw-commits": "^2.0.11", + "minimist": "^1.2.6" + }, "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=v18" + } + }, + "node_modules/@commitlint/resolve-extends": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.6.1.tgz", + "integrity": "sha512-ifRAQtHwK+Gj3Bxj/5chhc4L2LIc3s30lpsyW67yyjsETR6ctHAHRu1FSpt0KqahK5xESqoJ92v6XxoDRtjwEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/config-validator": "^18.6.1", + "@commitlint/types": "^18.6.1", + "import-fresh": "^3.0.0", + "lodash.mergewith": "^4.6.2", + "resolve-from": "^5.0.0", + "resolve-global": "^1.0.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/initials": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/initials/-/initials-5.4.3.tgz", - "integrity": "sha512-SkCxoo+llqJoOXQsVoJvruQhN4QCLOOtBvNsg7aO0W0MIYPWkJoGkpzKhXVuRBEsmI/QOBfkLMBFBpiHz22n4w==", + "node_modules/@commitlint/resolve-extends/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": "^14.13.1 || >=16.0.0" + "node": ">=8" + } + }, + "node_modules/@commitlint/rules": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.6.1.tgz", + "integrity": "sha512-kguM6HxZDtz60v/zQYOe0voAtTdGybWXefA1iidjWYmyUUspO1zBPQEmJZ05/plIAqCVyNUTAiRPWIBKLCrGew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@commitlint/ensure": "^18.6.1", + "@commitlint/message": "^18.6.1", + "@commitlint/to-lines": "^18.6.1", + "@commitlint/types": "^18.6.1", + "execa": "^5.0.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/lorelei": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/lorelei/-/lorelei-5.4.3.tgz", - "integrity": "sha512-/Zn8lzt0ZCc/S2iPfrSbHqJLa6wh5AoB5S9LclhOmksZqU7RLcjF86wyrwcbg/QZFxMSxvPr8ihL7SbkbRHh3g==", + "node_modules/@commitlint/to-lines": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.6.1.tgz", + "integrity": "sha512-Gl+orGBxYSNphx1+83GYeNy5N0dQsHBQ9PJMriaLQDB51UQHCVLBT/HBdOx5VaYksivSf5Os55TLePbRLlW50Q==", + "dev": true, "license": "MIT", "engines": { - "node": "^14.13.1 || >=16.0.0" + "node": ">=v18" + } + }, + "node_modules/@commitlint/top-level": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.6.1.tgz", + "integrity": "sha512-HyiHQZUTf0+r0goTCDs/bbVv/LiiQ7AVtz6KIar+8ZrseB9+YJAIo8HQ2IC2QT1y3N1lbW6OqVEsTHjbT6hGSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^5.0.0" }, - "peerDependencies": { - "@dicebear/core": "^5.0.0" + "engines": { + "node": ">=v18" } }, - "node_modules/@dicebear/lorelei-neutral": { + "node_modules/@commitlint/types": { + "version": "18.6.1", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.6.1.tgz", + "integrity": "sha512-gwRLBLra/Dozj2OywopeuHj2ac26gjGkz2cZ+86cTJOdtWfiRRr4+e77ZDAGc6MDWxaWheI+mAV5TLWWRwqrFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0" + }, + "engines": { + "node": ">=v18" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@dicebear/adventurer": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/lorelei-neutral/-/lorelei-neutral-5.4.3.tgz", - "integrity": "sha512-eJdnpIAUPXfYiXr+9Kt4r8dDKGiDKc3SliXcqwcEiHnM54k6p6EnAM54SxbxgVsGfKfhB4RxZo2CX1CiENwxUA==", + "resolved": "https://registry.npmjs.org/@dicebear/adventurer/-/adventurer-5.4.3.tgz", + "integrity": "sha512-PYv82wc1kwhFeyvZYKUIzOoaSNvRj5yn72yRCTlT3K4qoChKPGIJ7K/GoFoNX3Cj0XAWSkp0shPsGFtpU1o3Kg==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2753,10 +2988,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/micah": { + "node_modules/@dicebear/adventurer-neutral": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/micah/-/micah-5.4.3.tgz", - "integrity": "sha512-2seJqhL2a/AV233nPVuFkY7IBW9EUjB+eRYQTYHqkOfgbnExtS7t7jjC57Ot7+FHvvrMjH43r96CKBvMWHKfJw==", + "resolved": "https://registry.npmjs.org/@dicebear/adventurer-neutral/-/adventurer-neutral-5.4.3.tgz", + "integrity": "sha512-FdprrUE0zXRlx4UAu5ED+wFkxwNSQSJug+lvNjTQtA675c52kkuct/U9eCcD4vcSAd38v4IIKVBMLP/1tEBVXQ==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2765,10 +3000,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/miniavs": { + "node_modules/@dicebear/avataaars": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/miniavs/-/miniavs-5.4.3.tgz", - "integrity": "sha512-+L3YCUuqf1ufPbAMgVGAXPGT81a+4gPJKPn3mqYSpBY6l4xUUku8MPg0K3JDU1T/801ABT4VXkcIlHwgEz2HIg==", + "resolved": "https://registry.npmjs.org/@dicebear/avataaars/-/avataaars-5.4.3.tgz", + "integrity": "sha512-/zC/IPCI/JOsR6gUmXLo9HH2GFujHSvWfqYf23/n8V4e9Dm0gJ3RtZ/g9Kn+6Rkd8hiNp0BUzjlSEwiONEVOug==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2777,10 +3012,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/open-peeps": { + "node_modules/@dicebear/avataaars-neutral": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/open-peeps/-/open-peeps-5.4.3.tgz", - "integrity": "sha512-Xp/7uJBv+iCLG06e7+8KOVI7uDjk5YVj0cZpa6O+03kRF2sdglG8SBKPdT+l5iIxhAK0Yth4vzZZpjsiEqD+Jg==", + "resolved": "https://registry.npmjs.org/@dicebear/avataaars-neutral/-/avataaars-neutral-5.4.3.tgz", + "integrity": "sha512-3hV80zzqpn2tHaitRqRqokxJ/h5vD4YXwMtdvXJq6AKCVcUiVi9ecHVP3GU4uU2zPSk7VsbadVQMXy7/Z47nRw==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2789,10 +3024,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/personas": { + "node_modules/@dicebear/big-ears": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/personas/-/personas-5.4.3.tgz", - "integrity": "sha512-Anb1ICUcA/inrGV3FxGV9KJhsOLegpFo5VOWqyqZ/EbpVf77rgNZCj7GfwyzWuSYMKsnURpbxdTVlCn2LigHeA==", + "resolved": "https://registry.npmjs.org/@dicebear/big-ears/-/big-ears-5.4.3.tgz", + "integrity": "sha512-MXr0m0JNAGbxDt/onoIeIclOgUZbM1eHzH+i+uyz+9mK10IZPysV5i71cL84ZFDBEEWMKVi3uWfr1sCdck3+eQ==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2801,10 +3036,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/pixel-art": { + "node_modules/@dicebear/big-ears-neutral": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/pixel-art/-/pixel-art-5.4.3.tgz", - "integrity": "sha512-itCXZoH5jiVq+yhRMlWyol1TiVJgx/mlZZFcEmIO2tye1QSRWG9fhC4OGln7uvQ8GpLx1cRcVBdHVFA83nuYkQ==", + "resolved": "https://registry.npmjs.org/@dicebear/big-ears-neutral/-/big-ears-neutral-5.4.3.tgz", + "integrity": "sha512-uMt1MvnQ6pCL0u3rpjeEjryLs5MLLcYkAkidFZ6CpziiQyehkaI/1PwZlv7/zG7CD5wZTmUQKZ5+0U4fWR8rwg==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2813,10 +3048,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/pixel-art-neutral": { + "node_modules/@dicebear/big-smile": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/pixel-art-neutral/-/pixel-art-neutral-5.4.3.tgz", - "integrity": "sha512-giztLosESV1u7Slkl+B/wLSuWTAvhXvVHHmvbM+jouWTM2prKlI+pyrO/02bRl4rXcW49OHmUVIZLyBFZXpaWA==", + "resolved": "https://registry.npmjs.org/@dicebear/big-smile/-/big-smile-5.4.3.tgz", + "integrity": "sha512-JIBi6L9CcEhLE83CpHw/JskF7kmD4t+vIeAU95WNPm3c98lLe7h2k2ocDVnZEKpBUqxKG3lk0rFXoYM5ovSpLw==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2825,10 +3060,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/shapes": { + "node_modules/@dicebear/bottts": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/shapes/-/shapes-5.4.3.tgz", - "integrity": "sha512-42l2bpAufWkoqW1qzj0A4s5vwqoEtTUfjAl5/WrqNiN9EUFIG05dz5po8CWVe7rmUBuwwRKxhj9zZ7yArtQQ6Q==", + "resolved": "https://registry.npmjs.org/@dicebear/bottts/-/bottts-5.4.3.tgz", + "integrity": "sha512-yhuIB+8ptZzZgDGYOqNXplELoU/In/5zeiqsHEkJtjAEJZd6BEDqUYMHnB1IgUAic9L3gvPt7t3cv2fPbrFDPQ==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2837,10 +3072,10 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@dicebear/thumbs": { + "node_modules/@dicebear/bottts-neutral": { "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@dicebear/thumbs/-/thumbs-5.4.3.tgz", - "integrity": "sha512-9R8MbVkRsOXPJK4+YZ8hx8WqAPls4Ngf1ol1AQvtsU3qXs4hgTsR9Wz2h9pf+IU3dV4JaF53ubGrxWdncKYZig==", + "resolved": "https://registry.npmjs.org/@dicebear/bottts-neutral/-/bottts-neutral-5.4.3.tgz", + "integrity": "sha512-92mHaeDDblsF5fxX6f8JIYEGxBdIOhnVPNh88XejHBW4RBSyCvXgGVMvmnh6j4YTRoTQHObTCrxdbll4LG4qtQ==", "license": "MIT", "engines": { "node": "^14.13.1 || >=16.0.0" @@ -2849,1044 +3084,1129 @@ "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalbazaar/bitstring": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@digitalbazaar/bitstring/-/bitstring-3.1.0.tgz", - "integrity": "sha512-Cii+Sl++qaexOvv3vchhgZFfSmtHPNIPzGegaq4ffPnflVXFu+V2qrJ17aL2+gfLxrlC/zazZFuAltyKTPq7eg==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "base64url-universal": "^2.0.0", - "pako": "^2.0.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@digitalbazaar/http-client": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-3.4.1.tgz", - "integrity": "sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==", - "license": "BSD-3-Clause", - "optional": true, + "node_modules/@dicebear/collection": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/collection/-/collection-5.4.3.tgz", + "integrity": "sha512-iy+NWb4LL0LtYzxizzpXX7iP1WWFFatlEc8WjAsP6y+eKPXz7DWX0k5uJDEbHmJXpHEpvmaowV8evcL07iHwdw==", + "license": "MIT", "dependencies": { - "ky": "^0.33.3", - "ky-universal": "^0.11.0", - "undici": "^5.21.2" + "@dicebear/adventurer": "5.4.3", + "@dicebear/adventurer-neutral": "5.4.3", + "@dicebear/avataaars": "5.4.3", + "@dicebear/avataaars-neutral": "5.4.3", + "@dicebear/big-ears": "5.4.3", + "@dicebear/big-ears-neutral": "5.4.3", + "@dicebear/big-smile": "5.4.3", + "@dicebear/bottts": "5.4.3", + "@dicebear/bottts-neutral": "5.4.3", + "@dicebear/croodles": "5.4.3", + "@dicebear/croodles-neutral": "5.4.3", + "@dicebear/fun-emoji": "5.4.3", + "@dicebear/icons": "5.4.3", + "@dicebear/identicon": "5.4.3", + "@dicebear/initials": "5.4.3", + "@dicebear/lorelei": "5.4.3", + "@dicebear/lorelei-neutral": "5.4.3", + "@dicebear/micah": "5.4.3", + "@dicebear/miniavs": "5.4.3", + "@dicebear/open-peeps": "5.4.3", + "@dicebear/personas": "5.4.3", + "@dicebear/pixel-art": "5.4.3", + "@dicebear/pixel-art-neutral": "5.4.3", + "@dicebear/shapes": "5.4.3", + "@dicebear/thumbs": "5.4.3" }, "engines": { - "node": ">=14.0" - } - }, - "node_modules/@digitalbazaar/http-client/node_modules/ky": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", - "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14.16" + "node": "^14.13.1 || >=16.0.0" }, - "funding": { - "url": "https://github.com/sindresorhus/ky?sponsor=1" + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalbazaar/http-client/node_modules/ky-universal": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.11.0.tgz", - "integrity": "sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==", + "node_modules/@dicebear/converter": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/converter/-/converter-5.4.3.tgz", + "integrity": "sha512-7gXwzNfQf5v7JVOyeuOetP59NxYewOLr6QNZaV/+/Did6B6dUJqvAsHWOhmf/JEWJFDkh/6cyGZEgpM4eSxW4A==", "license": "MIT", - "optional": true, "dependencies": { - "abort-controller": "^3.0.0", - "node-fetch": "^3.2.10" + "@types/json-schema": "^7.0.7", + "tmp-promise": "^3.0.3" }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/ky-universal?sponsor=1" + "node": "^14.13.1 || >=16.0.0" }, "peerDependencies": { - "ky": ">=0.31.4", - "web-streams-polyfill": ">=3.2.1" + "@resvg/resvg-js": "^2.0.0", + "exiftool-vendored": "^16 || ^17 || ^18 || ^19 || ^20 || ^21", + "sharp": "^0.32.6" }, "peerDependenciesMeta": { - "web-streams-polyfill": { + "@resvg/resvg-js": { + "optional": true + }, + "exiftool-vendored": { + "optional": true + }, + "sharp": { "optional": true } } }, - "node_modules/@digitalbazaar/http-client/node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "node_modules/@dicebear/core": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/core/-/core-5.4.3.tgz", + "integrity": "sha512-qOOlqdVD1CWT0VFxABADP62oPF8WosfE+OkREYD+vZ3T7rq8kauHAUbGkvwRoB60uMld7kfZzEJNhjR7QI22IQ==", "license": "MIT", - "optional": true, "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" + "@dicebear/converter": "5.4.3" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" + "node": "^14.13.1 || >=16.0.0" } }, - "node_modules/@digitalbazaar/http-client/node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "node_modules/@dicebear/croodles": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/croodles/-/croodles-5.4.3.tgz", + "integrity": "sha512-eP+BwAb7Pm8msS+hcIMyfleBYSqGngt2Ocu/cNqZV2kSLkfIOrogSb4VJTkVtwbJd2Gpt41d+AxW71QSLN2ARQ==", "license": "MIT", - "optional": true, - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, "engines": { - "node": ">=14.0" - } - }, - "node_modules/@digitalbazaar/security-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@digitalbazaar/security-context/-/security-context-1.0.1.tgz", - "integrity": "sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@digitalbazaar/vc": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@digitalbazaar/vc/-/vc-5.0.0.tgz", - "integrity": "sha512-XmLM7Ag5W+XidGnFuxFIyUFSMnHnWEMJlHei602GG94+WzFJ6Ik8txzPQL8T18egSoiTsd1VekymbIlSimhuaQ==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "credentials-context": "^2.0.0", - "jsonld": "^8.0.0", - "jsonld-signatures": "^11.0.0" + "node": "^14.13.1 || >=16.0.0" }, - "engines": { - "node": ">=14" + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalbazaar/vc-status-list": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@digitalbazaar/vc-status-list/-/vc-status-list-7.1.0.tgz", - "integrity": "sha512-p5uxKJlX13N8TcTuv9qFDeej+6bndU+Rh1Cez2MT+bXQE6Jpn5t336FBSHmcECB4yUfZQpkmV/LOcYU4lW8Ojw==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@digitalbazaar/bitstring": "^3.0.0", - "@digitalbazaar/vc": "^5.0.0", - "@digitalbazaar/vc-status-list-context": "^3.0.1", - "credentials-context": "^2.0.0" - }, + "node_modules/@dicebear/croodles-neutral": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/croodles-neutral/-/croodles-neutral-5.4.3.tgz", + "integrity": "sha512-Z4wGeeUEdZjer6xKy5jimgDXQtUnW1OKQuV+LjA6bgyLTB/QmtmspZHzqCOgwZFQAydqcUoxEFWmrIpwNLujBA==", + "license": "MIT", "engines": { - "node": ">=16" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalbazaar/vc-status-list-context": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@digitalbazaar/vc-status-list-context/-/vc-status-list-context-3.1.1.tgz", - "integrity": "sha512-cMVtd+EV+4KN2kUG4/vsV74JVsGE6dcpod6zRoFB/AJA2W/sZbJqR44KL3G6P262+GcAECNhtnSsKsTnQ6y8+w==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/@digitalcredentials/base58-universal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@digitalcredentials/base58-universal/-/base58-universal-1.0.1.tgz", - "integrity": "sha512-1xKdJnfITMvrF/sCgwBx2C4p7qcNAARyIvrAOZGqIHmBaT/hAenpC8bf44qVY+UIMuCYP23kqpIfJQebQDThDQ==", - "license": "BSD-3-Clause", - "optional": true, + "node_modules/@dicebear/fun-emoji": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/fun-emoji/-/fun-emoji-5.4.3.tgz", + "integrity": "sha512-La4XonWV0abaIiE4Dj0OOtH7tH5VC1tiXmcapJJ69d3y6YfJnedyhHzD+Xk2gnWFD/X0mnmLJua++BGSrv7YkQ==", + "license": "MIT", "engines": { - "node": ">=12" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/base64url-universal": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@digitalcredentials/base64url-universal/-/base64url-universal-2.0.6.tgz", - "integrity": "sha512-QJyK6xS8BYNnkKLhEAgQc6Tb9DMe+GkHnBAWJKITCxVRXJAFLhJnr+FsJnCThS3x2Y0UiiDAXoWjwMqtUrp4Kg==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "base64url": "^3.0.1" - }, + "node_modules/@dicebear/icons": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/icons/-/icons-5.4.3.tgz", + "integrity": "sha512-p173l+PkZBrVtUTgSFVD9dHLc9djkRtxp+tLNsGYkBjeeWmJ/kxtPH5iG7MOp1pUnCHRkWJrLyysV51sgcrNug==", + "license": "MIT", "engines": { - "node": ">=14" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/bitstring": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@digitalcredentials/bitstring/-/bitstring-2.0.1.tgz", - "integrity": "sha512-9priXvsEJGI4LYHPwLqf5jv9HtQGlG0MgeuY8Q4NHN+xWz5rYMylh1TYTVThKa3XI6xF2pR2oEfKZD21eWXveQ==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@digitalcredentials/base64url-universal": "^2.0.2", - "pako": "^2.0.4" - }, + "node_modules/@dicebear/identicon": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/identicon/-/identicon-5.4.3.tgz", + "integrity": "sha512-it9tHCJ9UWdSwDzbYZVDhx8mZMQKeKXIWzmOWwS/aGHM3aONn8Kjxd6m5rnQGWFVzxxUjedrIOZxFhWuogzYOw==", + "license": "MIT", "engines": { - "node": ">=14" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/ed25519-signature-2020": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@digitalcredentials/ed25519-signature-2020/-/ed25519-signature-2020-3.0.2.tgz", - "integrity": "sha512-R8IrR21Dh+75CYriQov3nVHKaOVusbxfk9gyi6eCAwLHKn6fllUt+2LQfuUrL7Ts/sGIJqQcev7YvkX9GvyYRA==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@digitalcredentials/base58-universal": "^1.0.1", - "@digitalcredentials/ed25519-verification-key-2020": "^3.1.1", - "@digitalcredentials/jsonld-signatures": "^9.3.1", - "ed25519-signature-2018-context": "^1.1.0", - "ed25519-signature-2020-context": "^1.0.1" - }, + "node_modules/@dicebear/initials": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/initials/-/initials-5.4.3.tgz", + "integrity": "sha512-SkCxoo+llqJoOXQsVoJvruQhN4QCLOOtBvNsg7aO0W0MIYPWkJoGkpzKhXVuRBEsmI/QOBfkLMBFBpiHz22n4w==", + "license": "MIT", "engines": { - "node": ">=14" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/ed25519-signature-2020/node_modules/@digitalcredentials/ed25519-verification-key-2020": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@digitalcredentials/ed25519-verification-key-2020/-/ed25519-verification-key-2020-3.2.2.tgz", - "integrity": "sha512-ZfxNFZlA379MZpf+gV2tUYyiZ15eGVgjtCQLWlyu3frWxsumUgv++o0OJlMnrDsWGwzFMRrsXcosd5+752rLOA==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@digitalcredentials/base58-universal": "^1.0.1", - "@stablelib/ed25519": "^1.0.1", - "base64url-universal": "^1.1.0", - "crypto-ld": "^6.0.0" - }, + "node_modules/@dicebear/lorelei": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/lorelei/-/lorelei-5.4.3.tgz", + "integrity": "sha512-/Zn8lzt0ZCc/S2iPfrSbHqJLa6wh5AoB5S9LclhOmksZqU7RLcjF86wyrwcbg/QZFxMSxvPr8ihL7SbkbRHh3g==", + "license": "MIT", "engines": { - "node": ">=14" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/ed25519-signature-2020/node_modules/base64url-universal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/base64url-universal/-/base64url-universal-1.1.0.tgz", - "integrity": "sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "base64url": "^3.0.0" + "node_modules/@dicebear/lorelei-neutral": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/lorelei-neutral/-/lorelei-neutral-5.4.3.tgz", + "integrity": "sha512-eJdnpIAUPXfYiXr+9Kt4r8dDKGiDKc3SliXcqwcEiHnM54k6p6EnAM54SxbxgVsGfKfhB4RxZo2CX1CiENwxUA==", + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" + } + }, + "node_modules/@dicebear/micah": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/micah/-/micah-5.4.3.tgz", + "integrity": "sha512-2seJqhL2a/AV233nPVuFkY7IBW9EUjB+eRYQTYHqkOfgbnExtS7t7jjC57Ot7+FHvvrMjH43r96CKBvMWHKfJw==", + "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/ed25519-signature-2020/node_modules/crypto-ld": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/crypto-ld/-/crypto-ld-6.0.0.tgz", - "integrity": "sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA==", - "license": "BSD-3-Clause", - "optional": true, + "node_modules/@dicebear/miniavs": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/miniavs/-/miniavs-5.4.3.tgz", + "integrity": "sha512-+L3YCUuqf1ufPbAMgVGAXPGT81a+4gPJKPn3mqYSpBY6l4xUUku8MPg0K3JDU1T/801ABT4VXkcIlHwgEz2HIg==", + "license": "MIT", "engines": { - "node": ">=8.3.0" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/ed25519-verification-key-2020": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@digitalcredentials/ed25519-verification-key-2020/-/ed25519-verification-key-2020-4.0.0.tgz", - "integrity": "sha512-GrfITgp1guFbExZckj2q6LOxxm08PFSScr0lBYtDRezJa6CTpA9XQ8yXSSXE3LvpEi5/2uOMFxxIfKAtL1J2ww==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@digitalcredentials/keypair": "^1.0.5", - "@noble/ed25519": "^1.7.1", - "base-x": "^4.0.0" + "node_modules/@dicebear/open-peeps": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/open-peeps/-/open-peeps-5.4.3.tgz", + "integrity": "sha512-Xp/7uJBv+iCLG06e7+8KOVI7uDjk5YVj0cZpa6O+03kRF2sdglG8SBKPdT+l5iIxhAK0Yth4vzZZpjsiEqD+Jg==", + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" + } + }, + "node_modules/@dicebear/personas": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/personas/-/personas-5.4.3.tgz", + "integrity": "sha512-Anb1ICUcA/inrGV3FxGV9KJhsOLegpFo5VOWqyqZ/EbpVf77rgNZCj7GfwyzWuSYMKsnURpbxdTVlCn2LigHeA==", + "license": "MIT", "engines": { - "node": ">=18" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/http-client": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@digitalcredentials/http-client/-/http-client-1.2.2.tgz", - "integrity": "sha512-YOwaE+vUDSwiDhZT0BbXSWVg+bvp1HA1eg/gEc8OCwCOj9Bn9FRQdu8P9Y/fnYqyFCioDwwTRzGxgJLl50baEg==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "ky": "^0.25.1", - "ky-universal": "^0.8.2" + "node_modules/@dicebear/pixel-art": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/pixel-art/-/pixel-art-5.4.3.tgz", + "integrity": "sha512-itCXZoH5jiVq+yhRMlWyol1TiVJgx/mlZZFcEmIO2tye1QSRWG9fhC4OGln7uvQ8GpLx1cRcVBdHVFA83nuYkQ==", + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" + } + }, + "node_modules/@dicebear/pixel-art-neutral": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/pixel-art-neutral/-/pixel-art-neutral-5.4.3.tgz", + "integrity": "sha512-giztLosESV1u7Slkl+B/wLSuWTAvhXvVHHmvbM+jouWTM2prKlI+pyrO/02bRl4rXcW49OHmUVIZLyBFZXpaWA==", + "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/jsonld": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@digitalcredentials/jsonld/-/jsonld-6.0.0.tgz", - "integrity": "sha512-5tTakj0/GsqAJi8beQFVMQ97wUJZnuxViW9xRuAATL6eOBIefGBwHkVryAgEq2I4J/xKgb/nEyw1ZXX0G8wQJQ==", - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@digitalcredentials/http-client": "^1.0.0", - "@digitalcredentials/rdf-canonize": "^1.0.0", - "canonicalize": "^1.0.1", - "lru-cache": "^6.0.0" + "node_modules/@dicebear/shapes": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/shapes/-/shapes-5.4.3.tgz", + "integrity": "sha512-42l2bpAufWkoqW1qzj0A4s5vwqoEtTUfjAl5/WrqNiN9EUFIG05dz5po8CWVe7rmUBuwwRKxhj9zZ7yArtQQ6Q==", + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" + } + }, + "node_modules/@dicebear/thumbs": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@dicebear/thumbs/-/thumbs-5.4.3.tgz", + "integrity": "sha512-9R8MbVkRsOXPJK4+YZ8hx8WqAPls4Ngf1ol1AQvtsU3qXs4hgTsR9Wz2h9pf+IU3dV4JaF53ubGrxWdncKYZig==", + "license": "MIT", "engines": { - "node": ">=12" + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "@dicebear/core": "^5.0.0" } }, - "node_modules/@digitalcredentials/jsonld-signatures": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.4.0.tgz", - "integrity": "sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew==", + "node_modules/@digitalbazaar/bitstring": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/bitstring/-/bitstring-3.1.0.tgz", + "integrity": "sha512-Cii+Sl++qaexOvv3vchhgZFfSmtHPNIPzGegaq4ffPnflVXFu+V2qrJ17aL2+gfLxrlC/zazZFuAltyKTPq7eg==", "license": "BSD-3-Clause", "optional": true, "dependencies": { - "@digitalbazaar/security-context": "^1.0.0", - "@digitalcredentials/jsonld": "^6.0.0", - "fast-text-encoding": "^1.0.3", - "isomorphic-webcrypto": "^2.3.8", - "serialize-error": "^8.0.1" + "base64url-universal": "^2.0.0", + "pako": "^2.0.4" }, "engines": { - "node": ">=18" + "node": ">=16" } }, - "node_modules/@digitalcredentials/jsonld/node_modules/canonicalize": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", - "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/@digitalcredentials/jsonld/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", + "node_modules/@digitalbazaar/http-client": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@digitalbazaar/http-client/-/http-client-3.4.1.tgz", + "integrity": "sha512-Ahk1N+s7urkgj7WvvUND5f8GiWEPfUw0D41hdElaqLgu8wZScI8gdI0q+qWw5N1d35x7GCRH2uk9mi+Uzo9M3g==", + "license": "BSD-3-Clause", "optional": true, "dependencies": { - "yallist": "^4.0.0" + "ky": "^0.33.3", + "ky-universal": "^0.11.0", + "undici": "^5.21.2" }, "engines": { - "node": ">=10" + "node": ">=14.0" } }, - "node_modules/@digitalcredentials/keypair": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@digitalcredentials/keypair/-/keypair-1.0.5.tgz", - "integrity": "sha512-g0QvhJMTSFCoUkEvSeggwVTJa2jFkQXjf/mpTn9sePkz+5OouMEDfXUWL61juTaxK5JWPEFc0PKlolXzHaHHHQ==", + "node_modules/@digitalbazaar/http-client/node_modules/ky": { + "version": "0.33.3", + "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", + "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", "license": "MIT", "optional": true, "engines": { - "node": ">=16.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" } }, - "node_modules/@digitalcredentials/open-badges-context": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@digitalcredentials/open-badges-context/-/open-badges-context-2.1.0.tgz", - "integrity": "sha512-VK7X5u6OoBFxkyIFplNqUPVbo+8vFSAEoam8tSozpj05KPfcGw41Tp5p9fqMnY38oPfwtZR2yDNSctj/slrE0A==", + "node_modules/@digitalbazaar/http-client/node_modules/ky-universal": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/ky-universal/-/ky-universal-0.11.0.tgz", + "integrity": "sha512-65KyweaWvk+uKKkCrfAf+xqN2/epw1IJDtlyCPxYffFCMR8u1sp2U65NtWpnozYfZxQ6IUzIlvUcw+hQ82U2Xw==", "license": "MIT", - "optional": true - }, - "node_modules/@digitalcredentials/rdf-canonize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@digitalcredentials/rdf-canonize/-/rdf-canonize-1.0.0.tgz", - "integrity": "sha512-z8St0Ex2doecsExCFK1uI4gJC+a5EqYYu1xpRH1pKmqSS9l/nxfuVxexNFyaeEum4dUdg1EetIC2rTwLIFhPRA==", - "license": "BSD-3-Clause", "optional": true, "dependencies": { - "fast-text-encoding": "^1.0.3", - "isomorphic-webcrypto": "^2.3.8" + "abort-controller": "^3.0.0", + "node-fetch": "^3.2.10" }, "engines": { - "node": ">=12" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky-universal?sponsor=1" + }, + "peerDependencies": { + "ky": ">=0.31.4", + "web-streams-polyfill": ">=3.2.1" + }, + "peerDependenciesMeta": { + "web-streams-polyfill": { + "optional": true + } } }, - "node_modules/@digitalcredentials/vc": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@digitalcredentials/vc/-/vc-6.0.1.tgz", - "integrity": "sha512-TZgLoi00Jc9uv3b6jStH+G8+bCqpHIqFw9DYODz+fVjNh197ksvcYqSndUDHa2oi0HCcK+soI8j4ba3Sa4Pl4w==", - "license": "BSD-3-Clause", + "node_modules/@digitalbazaar/http-client/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", "optional": true, "dependencies": { - "@digitalbazaar/vc-status-list": "^7.0.0", - "@digitalcredentials/ed25519-signature-2020": "^3.0.2", - "@digitalcredentials/jsonld": "^6.0.0", - "@digitalcredentials/jsonld-signatures": "^9.3.2", - "@digitalcredentials/open-badges-context": "^2.1.0", - "@digitalcredentials/vc-status-list": "^5.0.2", - "credentials-context": "^2.0.0", - "fix-esm": "^1.0.1" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": ">=12" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, - "node_modules/@digitalcredentials/vc-status-list": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@digitalcredentials/vc-status-list/-/vc-status-list-5.0.2.tgz", - "integrity": "sha512-PI0N7SM0tXpaNLelbCNsMAi34AjOeuhUzMSYTkHdeqRPX7oT2F3ukyOssgr4koEqDxw9shHtxHu3fSJzrzcPMQ==", - "license": "BSD-3-Clause", + "node_modules/@digitalbazaar/http-client/node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "license": "MIT", "optional": true, "dependencies": { - "@digitalbazaar/vc-status-list-context": "^3.0.1", - "@digitalcredentials/bitstring": "^2.0.1", - "@digitalcredentials/vc": "^4.1.1", - "credentials-context": "^2.0.0" + "@fastify/busboy": "^2.0.0" }, "engines": { - "node": ">=14" + "node": ">=14.0" } }, - "node_modules/@digitalcredentials/vc-status-list/node_modules/@digitalcredentials/jsonld": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/@digitalcredentials/jsonld/-/jsonld-5.2.2.tgz", - "integrity": "sha512-hz7YR3kv6+8UUdgMyTGl1o8NjVKKwnMry/Rh/rWeAvwL+NqgoUHorWzI3rM+PW+MPFyDC0ieXStClt9n9D9SGA==", + "node_modules/@digitalbazaar/security-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@digitalbazaar/security-context/-/security-context-1.0.1.tgz", + "integrity": "sha512-0WZa6tPiTZZF8leBtQgYAfXQePFQp2z5ivpCEN/iZguYYZ0TB9qRmWtan5XH6mNFuusHtMcyIzAcReyE6rZPhA==", + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/@digitalbazaar/vc": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/vc/-/vc-5.0.0.tgz", + "integrity": "sha512-XmLM7Ag5W+XidGnFuxFIyUFSMnHnWEMJlHei602GG94+WzFJ6Ik8txzPQL8T18egSoiTsd1VekymbIlSimhuaQ==", "license": "BSD-3-Clause", "optional": true, "dependencies": { - "@digitalcredentials/http-client": "^1.0.0", - "@digitalcredentials/rdf-canonize": "^1.0.0", - "canonicalize": "^1.0.1", - "lru-cache": "^6.0.0" + "credentials-context": "^2.0.0", + "jsonld": "^8.0.0", + "jsonld-signatures": "^11.0.0" }, "engines": { - "node": ">=12" + "node": ">=14" } }, - "node_modules/@digitalcredentials/vc-status-list/node_modules/@digitalcredentials/vc": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@digitalcredentials/vc/-/vc-4.2.0.tgz", - "integrity": "sha512-8Rxpn77JghJN7noBQdcMuzm/tB8vhDwPoFepr3oGd5w+CyJxOk2RnBlgIGlAAGA+mALFWECPv1rANfXno+hdjA==", + "node_modules/@digitalbazaar/vc-status-list": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@digitalbazaar/vc-status-list/-/vc-status-list-7.1.0.tgz", + "integrity": "sha512-p5uxKJlX13N8TcTuv9qFDeej+6bndU+Rh1Cez2MT+bXQE6Jpn5t336FBSHmcECB4yUfZQpkmV/LOcYU4lW8Ojw==", "license": "BSD-3-Clause", "optional": true, "dependencies": { - "@digitalcredentials/jsonld": "^5.2.1", - "@digitalcredentials/jsonld-signatures": "^9.3.1", + "@digitalbazaar/bitstring": "^3.0.0", + "@digitalbazaar/vc": "^5.0.0", + "@digitalbazaar/vc-status-list-context": "^3.0.1", "credentials-context": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=16" } }, - "node_modules/@digitalcredentials/vc-status-list/node_modules/canonicalize": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", - "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", - "license": "Apache-2.0", + "node_modules/@digitalbazaar/vc-status-list-context": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@digitalbazaar/vc-status-list-context/-/vc-status-list-context-3.1.1.tgz", + "integrity": "sha512-cMVtd+EV+4KN2kUG4/vsV74JVsGE6dcpod6zRoFB/AJA2W/sZbJqR44KL3G6P262+GcAECNhtnSsKsTnQ6y8+w==", + "license": "BSD-3-Clause", "optional": true }, - "node_modules/@digitalcredentials/vc-status-list/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", + "node_modules/@digitalcredentials/base58-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@digitalcredentials/base58-universal/-/base58-universal-1.0.1.tgz", + "integrity": "sha512-1xKdJnfITMvrF/sCgwBx2C4p7qcNAARyIvrAOZGqIHmBaT/hAenpC8bf44qVY+UIMuCYP23kqpIfJQebQDThDQ==", + "license": "BSD-3-Clause", "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/@electron/asar": { - "version": "3.2.18", - "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.18.tgz", - "integrity": "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==", - "license": "MIT", + "node_modules/@digitalcredentials/base64url-universal": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@digitalcredentials/base64url-universal/-/base64url-universal-2.0.6.tgz", + "integrity": "sha512-QJyK6xS8BYNnkKLhEAgQc6Tb9DMe+GkHnBAWJKITCxVRXJAFLhJnr+FsJnCThS3x2Y0UiiDAXoWjwMqtUrp4Kg==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "commander": "^5.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - }, - "bin": { - "asar": "bin/asar.js" + "base64url": "^3.0.1" }, "engines": { - "node": ">=10.12.0" + "node": ">=14" } }, - "node_modules/@electron/asar/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==", - "license": "MIT", + "node_modules/@digitalcredentials/bitstring": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@digitalcredentials/bitstring/-/bitstring-2.0.1.tgz", + "integrity": "sha512-9priXvsEJGI4LYHPwLqf5jv9HtQGlG0MgeuY8Q4NHN+xWz5rYMylh1TYTVThKa3XI6xF2pR2oEfKZD21eWXveQ==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@electron/asar/node_modules/commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "license": "MIT", + "@digitalcredentials/base64url-universal": "^2.0.2", + "pako": "^2.0.4" + }, "engines": { - "node": ">= 6" + "node": ">=14" } }, - "node_modules/@electron/asar/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", + "node_modules/@digitalcredentials/ed25519-signature-2020": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@digitalcredentials/ed25519-signature-2020/-/ed25519-signature-2020-3.0.2.tgz", + "integrity": "sha512-R8IrR21Dh+75CYriQov3nVHKaOVusbxfk9gyi6eCAwLHKn6fllUt+2LQfuUrL7Ts/sGIJqQcev7YvkX9GvyYRA==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@digitalcredentials/base58-universal": "^1.0.1", + "@digitalcredentials/ed25519-verification-key-2020": "^3.1.1", + "@digitalcredentials/jsonld-signatures": "^9.3.1", + "ed25519-signature-2018-context": "^1.1.0", + "ed25519-signature-2020-context": "^1.0.1" }, "engines": { - "node": "*" + "node": ">=14" } }, - "node_modules/@electron/fuses": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", - "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", - "license": "MIT", + "node_modules/@digitalcredentials/ed25519-signature-2020/node_modules/@digitalcredentials/ed25519-verification-key-2020": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@digitalcredentials/ed25519-verification-key-2020/-/ed25519-verification-key-2020-3.2.2.tgz", + "integrity": "sha512-ZfxNFZlA379MZpf+gV2tUYyiZ15eGVgjtCQLWlyu3frWxsumUgv++o0OJlMnrDsWGwzFMRrsXcosd5+752rLOA==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "chalk": "^4.1.1", - "fs-extra": "^9.0.1", - "minimist": "^1.2.5" + "@digitalcredentials/base58-universal": "^1.0.1", + "@stablelib/ed25519": "^1.0.1", + "base64url-universal": "^1.1.0", + "crypto-ld": "^6.0.0" }, - "bin": { - "electron-fuses": "dist/bin.js" + "engines": { + "node": ">=14" } }, - "node_modules/@electron/fuses/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "license": "MIT", + "node_modules/@digitalcredentials/ed25519-signature-2020/node_modules/base64url-universal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/base64url-universal/-/base64url-universal-1.1.0.tgz", + "integrity": "sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "base64url": "^3.0.0" }, "engines": { - "node": ">=10" + "node": ">=8.3.0" } }, - "node_modules/@electron/node-gyp": { - "version": "10.2.0-electron.1", - "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", - "integrity": "sha512-CrYo6TntjpoMO1SHjl5Pa/JoUsECNqNdB7Kx49WLQpWzPw53eEITJ2Hs9fh/ryUYDn4pxZz11StaBYBrLFJdqg==", - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^8.1.0", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.2.1", - "nopt": "^6.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "tar": "^6.2.1", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, + "node_modules/@digitalcredentials/ed25519-signature-2020/node_modules/crypto-ld": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crypto-ld/-/crypto-ld-6.0.0.tgz", + "integrity": "sha512-XWL1LslqggNoaCI/m3I7HcvaSt9b2tYzdrXO+jHLUj9G1BvRfvV7ZTFDVY5nifYuIGAPdAGu7unPxLRustw3VA==", + "license": "BSD-3-Clause", + "optional": true, "engines": { - "node": ">=12.13.0" + "node": ">=8.3.0" } }, - "node_modules/@electron/node-gyp/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", + "node_modules/@digitalcredentials/ed25519-verification-key-2020": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@digitalcredentials/ed25519-verification-key-2020/-/ed25519-verification-key-2020-4.0.0.tgz", + "integrity": "sha512-GrfITgp1guFbExZckj2q6LOxxm08PFSScr0lBYtDRezJa6CTpA9XQ8yXSSXE3LvpEi5/2uOMFxxIfKAtL1J2ww==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "@digitalcredentials/keypair": "^1.0.5", + "@noble/ed25519": "^1.7.1", + "base-x": "^4.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/@electron/node-gyp/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", + "node_modules/@digitalcredentials/http-client": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@digitalcredentials/http-client/-/http-client-1.2.2.tgz", + "integrity": "sha512-YOwaE+vUDSwiDhZT0BbXSWVg+bvp1HA1eg/gEc8OCwCOj9Bn9FRQdu8P9Y/fnYqyFCioDwwTRzGxgJLl50baEg==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "brace-expansion": "^2.0.1" + "ky": "^0.25.1", + "ky-universal": "^0.8.2" }, "engines": { - "node": ">=10" + "node": ">=12.0.0" } }, - "node_modules/@electron/notarize": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", - "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", - "license": "MIT", + "node_modules/@digitalcredentials/jsonld": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@digitalcredentials/jsonld/-/jsonld-6.0.0.tgz", + "integrity": "sha512-5tTakj0/GsqAJi8beQFVMQ97wUJZnuxViW9xRuAATL6eOBIefGBwHkVryAgEq2I4J/xKgb/nEyw1ZXX0G8wQJQ==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "debug": "^4.1.1", - "fs-extra": "^9.0.1", - "promise-retry": "^2.0.1" + "@digitalcredentials/http-client": "^1.0.0", + "@digitalcredentials/rdf-canonize": "^1.0.0", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0" }, "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/@electron/notarize/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "license": "MIT", + "node_modules/@digitalcredentials/jsonld-signatures": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/@digitalcredentials/jsonld-signatures/-/jsonld-signatures-9.4.0.tgz", + "integrity": "sha512-DnR+HDTm7qpcDd0wcD1w6GdlAwfHjQSgu+ahion8REkCkkMRywF+CLunU7t8AZpFB2Gr/+N8naUtiEBNje1Oew==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@digitalbazaar/security-context": "^1.0.0", + "@digitalcredentials/jsonld": "^6.0.0", + "fast-text-encoding": "^1.0.3", + "isomorphic-webcrypto": "^2.3.8", + "serialize-error": "^8.0.1" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/@electron/osx-sign": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", - "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", - "license": "BSD-2-Clause", - "dependencies": { - "compare-version": "^0.1.2", - "debug": "^4.3.4", - "fs-extra": "^10.0.0", - "isbinaryfile": "^4.0.8", - "minimist": "^1.2.6", - "plist": "^3.0.5" - }, - "bin": { - "electron-osx-flat": "bin/electron-osx-flat.js", - "electron-osx-sign": "bin/electron-osx-sign.js" - }, - "engines": { - "node": ">=12.0.0" - } + "node_modules/@digitalcredentials/jsonld/node_modules/canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", + "license": "Apache-2.0", + "optional": true }, - "node_modules/@electron/osx-sign/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "license": "MIT", + "node_modules/@digitalcredentials/jsonld/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "node_modules/@digitalcredentials/keypair": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@digitalcredentials/keypair/-/keypair-1.0.5.tgz", + "integrity": "sha512-g0QvhJMTSFCoUkEvSeggwVTJa2jFkQXjf/mpTn9sePkz+5OouMEDfXUWL61juTaxK5JWPEFc0PKlolXzHaHHHQ==", "license": "MIT", + "optional": true, "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" + "node": ">=16.0" } }, - "node_modules/@electron/rebuild": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz", - "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==", + "node_modules/@digitalcredentials/open-badges-context": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@digitalcredentials/open-badges-context/-/open-badges-context-2.1.0.tgz", + "integrity": "sha512-VK7X5u6OoBFxkyIFplNqUPVbo+8vFSAEoam8tSozpj05KPfcGw41Tp5p9fqMnY38oPfwtZR2yDNSctj/slrE0A==", "license": "MIT", + "optional": true + }, + "node_modules/@digitalcredentials/rdf-canonize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@digitalcredentials/rdf-canonize/-/rdf-canonize-1.0.0.tgz", + "integrity": "sha512-z8St0Ex2doecsExCFK1uI4gJC+a5EqYYu1xpRH1pKmqSS9l/nxfuVxexNFyaeEum4dUdg1EetIC2rTwLIFhPRA==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", - "@malept/cross-spawn-promise": "^2.0.0", - "chalk": "^4.0.0", - "debug": "^4.1.1", - "detect-libc": "^2.0.1", - "fs-extra": "^10.0.0", - "got": "^11.7.0", - "node-abi": "^3.45.0", - "node-api-version": "^0.2.0", - "ora": "^5.1.0", - "read-binary-file-arch": "^1.0.6", - "semver": "^7.3.5", - "tar": "^6.0.5", - "yargs": "^17.0.1" - }, - "bin": { - "electron-rebuild": "lib/cli.js" + "fast-text-encoding": "^1.0.3", + "isomorphic-webcrypto": "^2.3.8" }, "engines": { - "node": ">=12.13.0" + "node": ">=12" } }, - "node_modules/@electron/rebuild/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "license": "MIT", + "node_modules/@digitalcredentials/vc": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@digitalcredentials/vc/-/vc-6.0.1.tgz", + "integrity": "sha512-TZgLoi00Jc9uv3b6jStH+G8+bCqpHIqFw9DYODz+fVjNh197ksvcYqSndUDHa2oi0HCcK+soI8j4ba3Sa4Pl4w==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "@digitalbazaar/vc-status-list": "^7.0.0", + "@digitalcredentials/ed25519-signature-2020": "^3.0.2", + "@digitalcredentials/jsonld": "^6.0.0", + "@digitalcredentials/jsonld-signatures": "^9.3.2", + "@digitalcredentials/open-badges-context": "^2.1.0", + "@digitalcredentials/vc-status-list": "^5.0.2", + "credentials-context": "^2.0.0", + "fix-esm": "^1.0.1" }, "engines": { "node": ">=12" } }, - "node_modules/@electron/universal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", - "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", - "license": "MIT", + "node_modules/@digitalcredentials/vc-status-list": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@digitalcredentials/vc-status-list/-/vc-status-list-5.0.2.tgz", + "integrity": "sha512-PI0N7SM0tXpaNLelbCNsMAi34AjOeuhUzMSYTkHdeqRPX7oT2F3ukyOssgr4koEqDxw9shHtxHu3fSJzrzcPMQ==", + "license": "BSD-3-Clause", + "optional": true, "dependencies": { - "@electron/asar": "^3.2.7", - "@malept/cross-spawn-promise": "^2.0.0", - "debug": "^4.3.1", - "dir-compare": "^4.2.0", - "fs-extra": "^11.1.1", - "minimatch": "^9.0.3", - "plist": "^3.1.0" + "@digitalbazaar/vc-status-list-context": "^3.0.1", + "@digitalcredentials/bitstring": "^2.0.1", + "@digitalcredentials/vc": "^4.1.1", + "credentials-context": "^2.0.0" }, "engines": { - "node": ">=16.4" + "node": ">=14" } }, - "node_modules/@electron/windows-sign": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", - "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", - "license": "BSD-2-Clause", + "node_modules/@digitalcredentials/vc-status-list/node_modules/@digitalcredentials/jsonld": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@digitalcredentials/jsonld/-/jsonld-5.2.2.tgz", + "integrity": "sha512-hz7YR3kv6+8UUdgMyTGl1o8NjVKKwnMry/Rh/rWeAvwL+NqgoUHorWzI3rM+PW+MPFyDC0ieXStClt9n9D9SGA==", + "license": "BSD-3-Clause", "optional": true, - "peer": true, "dependencies": { - "cross-dirname": "^0.1.0", - "debug": "^4.3.4", - "fs-extra": "^11.1.1", - "minimist": "^1.2.8", - "postject": "^1.0.0-alpha.6" - }, - "bin": { - "electron-windows-sign": "bin/electron-windows-sign.js" + "@digitalcredentials/http-client": "^1.0.0", + "@digitalcredentials/rdf-canonize": "^1.0.0", + "canonicalize": "^1.0.1", + "lru-cache": "^6.0.0" }, "engines": { - "node": ">=14.14" + "node": ">=12" } }, - "node_modules/@emnapi/core": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", - "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", - "dev": true, - "license": "MIT", + "node_modules/@digitalcredentials/vc-status-list/node_modules/@digitalcredentials/vc": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@digitalcredentials/vc/-/vc-4.2.0.tgz", + "integrity": "sha512-8Rxpn77JghJN7noBQdcMuzm/tB8vhDwPoFepr3oGd5w+CyJxOk2RnBlgIGlAAGA+mALFWECPv1rANfXno+hdjA==", + "license": "BSD-3-Clause", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.0.4", - "tslib": "^2.4.0" + "@digitalcredentials/jsonld": "^5.2.1", + "@digitalcredentials/jsonld-signatures": "^9.3.1", + "credentials-context": "^2.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } + "node_modules/@digitalcredentials/vc-status-list/node_modules/canonicalize": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/canonicalize/-/canonicalize-1.0.8.tgz", + "integrity": "sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==", + "license": "Apache-2.0", + "optional": true }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", - "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", - "dev": true, - "license": "MIT", + "node_modules/@digitalcredentials/vc-status-list/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "optional": true, "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], + "yallist": "^4.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/@electron/asar": { + "version": "3.2.18", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.18.tgz", + "integrity": "sha512-2XyvMe3N3Nrs8cV39IKELRHTYUWFKrmqqSY1U+GMlc0jvqjIVnoxhNd2H4JolWQncbJi1DCvb5TNxZuI2fEjWg==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, "engines": { - "node": ">=12" + "node": ">=10.12.0" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@electron/asar/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==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@electron/asar/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": ">= 6" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=12" + "node": "*" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@electron/fuses": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@electron/fuses/-/fuses-1.8.0.tgz", + "integrity": "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw==", "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "dependencies": { + "chalk": "^4.1.1", + "fs-extra": "^9.0.1", + "minimist": "^1.2.5" + }, + "bin": { + "electron-fuses": "dist/bin.js" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/@electron/fuses/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/@electron/node-gyp": { + "version": "10.2.0-electron.1", + "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "integrity": "sha512-CrYo6TntjpoMO1SHjl5Pa/JoUsECNqNdB7Kx49WLQpWzPw53eEITJ2Hs9fh/ryUYDn4pxZz11StaBYBrLFJdqg==", "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^8.1.0", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.2.1", + "nopt": "^6.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, "engines": { - "node": ">=12" + "node": ">=12.13.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@electron/node-gyp/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, "engines": { "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@electron/node-gyp/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=10" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], - "dev": true, + "node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", + "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, "engines": { "node": ">=12" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/rebuild": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.7.0.tgz", + "integrity": "sha512-VW++CNSlZwMYP7MyXEbrKjpzEwhB5kDNbzGtiPEjwYysqyTCF+YbNJ210Dj3AjWsGSV4iEEwNkmJN9yGZmVvmw==", + "license": "MIT", + "dependencies": { + "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron/rebuild/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", + "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.7", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/windows-sign": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@electron/windows-sign/-/windows-sign-1.2.2.tgz", + "integrity": "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ==", + "license": "BSD-2-Clause", + "optional": true, + "peer": true, + "dependencies": { + "cross-dirname": "^0.1.0", + "debug": "^4.3.4", + "fs-extra": "^11.1.1", + "minimist": "^1.2.8", + "postject": "^1.0.0-alpha.6" + }, + "bin": { + "electron-windows-sign": "bin/electron-windows-sign.js" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@emnapi/core": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ - "loong64" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "node_modules/@esbuild/android-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ - "mips64el" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "node_modules/@esbuild/android-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ - "ppc64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "node_modules/@esbuild/android-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ - "riscv64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ - "s390x" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -3894,16 +4214,220 @@ "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -3914,13 +4438,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -3931,13 +4472,30 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -3948,13 +4506,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -3965,13 +4523,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -3982,13 +4540,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -3999,7 +4557,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -6051,9 +6609,9 @@ } }, "node_modules/@expo/plist/node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", "license": "MIT", "optional": true, "peer": true, @@ -6615,9 +7173,9 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "license": "MIT", "engines": { "node": ">=12" @@ -6836,13 +7394,6 @@ "node": ">=8" } }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "devOptional": true, - "license": "BSD-3-Clause" - }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", @@ -7347,9 +7898,9 @@ "license": "MIT" }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", - "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", + "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==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7368,9 +7919,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", - "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "license": "MIT", "optional": true, "peer": true, @@ -7380,15 +7931,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", - "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.29", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", - "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", + "version": "0.3.30", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", + "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", "devOptional": true, "license": "MIT", "dependencies": { @@ -7849,14 +8400,14 @@ } }, "node_modules/@react-native/assets-registry": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.80.2.tgz", - "integrity": "sha512-+sI2zIM22amhkZqW+RpD3qDoopeRiezrTtZMP+Y3HI+6/2JbEq7DdyV/2YS1lrSSdyy3STW2V37Lt4dKqP0lEQ==", + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.81.0.tgz", + "integrity": "sha512-rZs8ziQ1YRV3Z5Mw5AR7YcgI3q1Ya9NIx6nyuZAT9wDSSjspSi+bww+Hargh/a4JfV2Ajcxpn9X9UiFJr1ddPw==", "license": "MIT", "optional": true, "peer": true, "engines": { - "node": ">=18" + "node": ">= 20.19.4" } }, "node_modules/@react-native/babel-plugin-codegen": { @@ -7957,27 +8508,27 @@ } }, "node_modules/@react-native/community-cli-plugin": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.80.2.tgz", - "integrity": "sha512-UBjsE+lv1YtThs56mgFaUdWv0jNE1oO58Lkbf3dn47F0e7YiTubIcvP6AnlaMhZF2Pmt9ky8J1jTpgItO9tGeg==", + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.81.0.tgz", + "integrity": "sha512-n04ACkCaLR54NmA/eWiDpjC16pHr7+yrbjQ6OEdRoXbm5EfL8FEre2kDAci7pfFdiSMpxdRULDlKpfQ+EV/GAQ==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "@react-native/dev-middleware": "0.80.2", - "chalk": "^4.0.0", + "@react-native/dev-middleware": "0.81.0", "debug": "^4.4.0", "invariant": "^2.2.4", - "metro": "^0.82.2", - "metro-config": "^0.82.2", - "metro-core": "^0.82.2", + "metro": "^0.83.1", + "metro-config": "^0.83.1", + "metro-core": "^0.83.1", "semver": "^7.1.3" }, "engines": { - "node": ">=18" + "node": ">= 20.19.4" }, "peerDependencies": { - "@react-native-community/cli": "*" + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" }, "peerDependenciesMeta": { "@react-native-community/cli": { @@ -7986,26 +8537,26 @@ } }, "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/debugger-frontend": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.80.2.tgz", - "integrity": "sha512-n3D88bqNk0bY+YjNxbM6giqva06xj+rgEfu91Pg+nJ0szSL2eLl7ULERJqI3hxFt0XGuTpTOxZgw/Po5maXa4g==", + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.81.0.tgz", + "integrity": "sha512-N/8uL2CGQfwiQRYFUNfmaYxRDSoSeOmFb56rb0PDnP3XbS5+X9ee7X4bdnukNHLGfkRdH7sVjlB8M5zE8XJOhw==", "license": "BSD-3-Clause", "optional": true, "peer": true, "engines": { - "node": ">=18" + "node": ">= 20.19.4" } }, "node_modules/@react-native/community-cli-plugin/node_modules/@react-native/dev-middleware": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.80.2.tgz", - "integrity": "sha512-8OeBEZNiApdbZaqTrrzeyFwXn/JwgJox7jdtjVAH56DggTVJXdbnyUjQ4ts6XAacEQgpFOAskoO730eyafOkAA==", + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.81.0.tgz", + "integrity": "sha512-J/HeC/+VgRyGECPPr9rAbe5S0OL6MCIrvrC/kgNKSME5+ZQLCiTpt3pdAoAMXwXiF9a02Nmido0DnyM1acXTIA==", "license": "MIT", "optional": true, "peer": true, "dependencies": { "@isaacs/ttlcache": "^1.4.1", - "@react-native/debugger-frontend": "0.80.2", + "@react-native/debugger-frontend": "0.81.0", "chrome-launcher": "^0.15.2", "chromium-edge-launcher": "^0.2.0", "connect": "^3.6.5", @@ -8017,7 +8568,7 @@ "ws": "^6.2.3" }, "engines": { - "node": ">=18" + "node": ">= 20.19.4" } }, "node_modules/@react-native/community-cli-plugin/node_modules/debug": { @@ -8160,52 +8711,216 @@ } }, "node_modules/@react-native/gradle-plugin": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.80.2.tgz", - "integrity": "sha512-C5/FYbIfCXPFjF/hIcWFKC9rEadDDhPMbxE7tarGR9tmYKyb9o7fYvfNe8fFgbCRKelMHP0ShATz3T73pHHDfA==", + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.81.0.tgz", + "integrity": "sha512-LGNtPXO1RKLws5ORRb4Q4YULi2qxM4qZRuARtwqM/1f2wyZVggqapoV0OXlaXaz+GiEd2ll3ROE4CcLN6J93jg==", "license": "MIT", "optional": true, "peer": true, "engines": { - "node": ">=18" + "node": ">= 20.19.4" } }, "node_modules/@react-native/js-polyfills": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.80.2.tgz", - "integrity": "sha512-f63M3paxHK92p6L9o+AY7hV/YojCZAhb+fdDpSfOtDtCngWbBhd6foJrO6IybzDFERxlwErupUg3pqr5w3KJWw==", + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.81.0.tgz", + "integrity": "sha512-whXZWIogzoGpqdyTjqT89M6DXmlOkWqNpWoVOAwVi8XFCMO+L7WTk604okIgO6gdGZcP1YtFpQf9JusbKrv/XA==", "license": "MIT", "optional": true, "peer": true, "engines": { - "node": ">=18" + "node": ">= 20.19.4" } }, - "node_modules/@react-native/normalize-colors": { - "version": "0.79.5", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.79.5.tgz", - "integrity": "sha512-nGXMNMclZgzLUxijQQ38Dm3IAEhgxuySAWQHnljFtfB0JdaMwpe0Ox9H7Tp2OgrEA+EMEv+Od9ElKlHwGKmmvQ==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/@react-native/virtualized-lists": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.80.2.tgz", - "integrity": "sha512-kXsIV2eB73QClbbH/z/lRhZkyj3Dke4tarM5w2yXSNwJthMPMfj4KqLZ6Lnf0nmPPjz7qo/voKtlrGqlM822Rg==", + "node_modules/@react-native/metro-babel-transformer": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.81.0.tgz", + "integrity": "sha512-Mwovr4jJ3JTnbHEQLhdcMvS82LjijpqCydXl1aH2N16WVCrE5oSNFiqTt6NpZBw9zkJX7nijsY+xeCy6m+KK3Q==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "invariant": "^2.2.4", + "@babel/core": "^7.25.2", + "@react-native/babel-preset": "0.81.0", + "hermes-parser": "0.29.1", "nullthrows": "^1.1.1" }, "engines": { - "node": ">=18" + "node": ">= 20.19.4" }, "peerDependencies": { - "@types/react": "^19.0.0", - "react": "*", + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-plugin-codegen": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.81.0.tgz", + "integrity": "sha512-MEMlW91+2Kk9GiObRP1Nc6oTdiyvmSEbPMSC6kzUzDyouxnh5/x28uyNySmB2nb6ivcbmQ0lxaU059+CZSkKXQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@babel/traverse": "^7.25.3", + "@react-native/codegen": "0.81.0" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/babel-preset": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/babel-preset/-/babel-preset-0.81.0.tgz", + "integrity": "sha512-RKMgCUGsso/2b32kgg24lB68LJ6qr2geLoSQTbisY6Usye0uXeXCgbZZDbILIX9upL4uzU4staMldRZ0v08F1g==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/plugin-proposal-export-default-from": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-default-from": "^7.24.7", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", + "@babel/plugin-transform-classes": "^7.25.4", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-flow-strip-types": "^7.25.2", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-react-display-name": "^7.24.7", + "@babel/plugin-transform-react-jsx": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-runtime": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-typescript": "^7.25.2", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/template": "^7.25.0", + "@react-native/babel-plugin-codegen": "0.81.0", + "babel-plugin-syntax-hermes-parser": "0.29.1", + "babel-plugin-transform-flow-enums": "^0.0.2", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/@react-native/codegen": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.0.tgz", + "integrity": "sha512-gPFutgtj8YqbwKKt3YpZKamUBGd9YZJV51Jq2aiDZ9oThkg1frUBa20E+Jdi7jKn982wjBMxAklAR85QGQ4xMA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "glob": "^7.1.1", + "hermes-parser": "0.29.1", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", + "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "hermes-parser": "0.29.1" + } + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@react-native/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "hermes-estree": "0.29.1" + } + }, + "node_modules/@react-native/metro-config": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/metro-config/-/metro-config-0.81.0.tgz", + "integrity": "sha512-5eqLP4TCERHGRYDJSZa//O98CGDFNNEwHVvhs65Msfy6hAoSdw5pAAuTrsQwmbTBp0Fkvu7Bx8BZDhiferZsHg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@react-native/js-polyfills": "0.81.0", + "@react-native/metro-babel-transformer": "0.81.0", + "metro-config": "^0.83.1", + "metro-runtime": "^0.83.1" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.79.5", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.79.5.tgz", + "integrity": "sha512-nGXMNMclZgzLUxijQQ38Dm3IAEhgxuySAWQHnljFtfB0JdaMwpe0Ox9H7Tp2OgrEA+EMEv+Od9ElKlHwGKmmvQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/@react-native/virtualized-lists": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.0.tgz", + "integrity": "sha512-p14QC5INHkbMZ96158sUxkSwN6zp138W11G+CRGoLJY4Q9WRJBCe7wHR5Owyy3XczQXrIih/vxAXwgYeZ2XByg==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.0", + "react": "*", "react-native": "*" }, "peerDependenciesMeta": { @@ -8215,9 +8930,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", - "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.4.tgz", + "integrity": "sha512-B2wfzCJ+ps/OBzRjeds7DlJumCU3rXMxJJS1vzURyj7+KBHGONm7c9q1TfdBl4vCuNMkDvARn3PBl2wZzuR5mw==", "cpu": [ "arm" ], @@ -8229,9 +8944,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", - "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.4.tgz", + "integrity": "sha512-FGJYXvYdn8Bs6lAlBZYT5n+4x0ciEp4cmttsvKAZc/c8/JiPaQK8u0c/86vKX8lA7OY/+37lIQSe0YoAImvBAA==", "cpu": [ "arm64" ], @@ -8269,9 +8984,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", - "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.4.tgz", + "integrity": "sha512-+ToyOMYnSfV8D+ckxO6NthPln/PDNp1P6INcNypfZ7muLmEvPKXqduUiD8DlJpMMT8LxHcE5W0dK9kXfJke9Zw==", "cpu": [ "arm64" ], @@ -8283,9 +8998,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", - "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.4.tgz", + "integrity": "sha512-cGT6ey/W+sje6zywbLiqmkfkO210FgRz7tepWAzzEVgQU8Hn91JJmQWNqs55IuglG8sJdzk7XfNgmGRtcYlo1w==", "cpu": [ "x64" ], @@ -8297,9 +9012,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", - "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.4.tgz", + "integrity": "sha512-9fhTJyOb275w5RofPSl8lpr4jFowd+H4oQKJ9XTYzD1JWgxdZKE8bA6d4npuiMemkecQOcigX01FNZNCYnQBdA==", "cpu": [ "arm" ], @@ -8311,9 +9026,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", - "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.4.tgz", + "integrity": "sha512-+6kCIM5Zjvz2HwPl/udgVs07tPMIp1VU2Y0c72ezjOvSvEfAIWsUgpcSDvnC7g9NrjYR6X9bZT92mZZ90TfvXw==", "cpu": [ "arm" ], @@ -8351,9 +9066,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", - "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.4.tgz", + "integrity": "sha512-mCBkjRZWhvjtl/x+Bd4fQkWZT8canStKDxGrHlBiTnZmJnWygGcvBylzLVCZXka4dco5ymkWhZlLwKCGFF4ivw==", "cpu": [ "loong64" ], @@ -8365,9 +9080,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", - "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.4.tgz", + "integrity": "sha512-YMdz2phOTFF+Z66dQfGf0gmeDSi5DJzY5bpZyeg9CPBkV9QDzJ1yFRlmi/j7WWRf3hYIWrOaJj5jsfwgc8GTHQ==", "cpu": [ "ppc64" ], @@ -8379,9 +9094,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", - "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.4.tgz", + "integrity": "sha512-r0WKLSfFAK8ucG024v2yiLSJMedoWvk8yWqfNICX28NHDGeu3F/wBf8KG6mclghx4FsLePxJr/9N8rIj1PtCnw==", "cpu": [ "riscv64" ], @@ -8393,9 +9108,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", - "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.4.tgz", + "integrity": "sha512-IaizpPP2UQU3MNyPH1u0Xxbm73D+4OupL0bjo4Hm0496e2wg3zuvoAIhubkD1NGy9fXILEExPQy87mweujEatA==", "cpu": [ "riscv64" ], @@ -8407,9 +9122,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", - "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.4.tgz", + "integrity": "sha512-aCM29orANR0a8wk896p6UEgIfupReupnmISz6SUwMIwTGaTI8MuKdE0OD2LvEg8ondDyZdMvnaN3bW4nFbATPA==", "cpu": [ "s390x" ], @@ -8460,9 +9175,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", - "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.4.tgz", + "integrity": "sha512-OiEa5lRhiANpv4SfwYVgQ3opYWi/QmPDC5ve21m8G9pf6ZO+aX1g2EEF1/IFaM1xPSP7mK0msTRXlPs6mIagkg==", "cpu": [ "ia32" ], @@ -8573,9 +9288,9 @@ "license": "MIT" }, "node_modules/@sinclair/typebox": { - "version": "0.34.38", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.38.tgz", - "integrity": "sha512-HpkxMmc2XmZKhvaKIZZThlHmx1L0I/V1hWK1NubtlFnr6ZqdiOpV72TKudZUNQjZNsyDBay72qFEhEvb+bcwcA==", + "version": "0.34.40", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.40.tgz", + "integrity": "sha512-gwBNIP8ZAYev/ORDWW0QvxdwPXwxBtLsdsJgSc7eDIRt8ubP+rxUBzPsrwnu16fgEF8Bx4lh/+mvQvJzcTM6Kw==", "dev": true, "license": "MIT" }, @@ -8591,6 +9306,19 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -9440,9 +10168,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.10.tgz", - "integrity": "sha512-iAFpG6DokED3roLSP0K+ybeDdIX6Bc0Vd3mLW5uDqThPWtNos3E+EqOM11mPQHKzfWHqEBuLjIlsBQQ8CsISmQ==", + "version": "20.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", + "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -10605,53 +11333,53 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", - "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", + "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/shared": "3.5.18", + "@babel/parser": "^7.25.3", + "@vue/shared": "3.5.13", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", - "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", + "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.18", - "@vue/shared": "3.5.18" + "@vue/compiler-core": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", - "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", + "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.0", - "@vue/compiler-core": "3.5.18", - "@vue/compiler-dom": "3.5.18", - "@vue/compiler-ssr": "3.5.18", - "@vue/shared": "3.5.18", + "@babel/parser": "^7.25.3", + "@vue/compiler-core": "3.5.13", + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13", "estree-walker": "^2.0.2", - "magic-string": "^0.30.17", - "postcss": "^8.5.6", - "source-map-js": "^1.2.1" + "magic-string": "^0.30.11", + "postcss": "^8.4.48", + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", - "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", + "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.18", - "@vue/shared": "3.5.18" + "@vue/compiler-dom": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/devtools-api": { @@ -10906,53 +11634,53 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", - "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", + "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.18" + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", - "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", + "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.18", - "@vue/shared": "3.5.18" + "@vue/reactivity": "3.5.13", + "@vue/shared": "3.5.13" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", - "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", + "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.18", - "@vue/runtime-core": "3.5.18", - "@vue/shared": "3.5.18", + "@vue/reactivity": "3.5.13", + "@vue/runtime-core": "3.5.13", + "@vue/shared": "3.5.13", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", - "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", + "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.18", - "@vue/shared": "3.5.18" + "@vue/compiler-ssr": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { - "vue": "3.5.18" + "vue": "3.5.13" } }, "node_modules/@vue/shared": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", - "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", + "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", "license": "MIT" }, "node_modules/@vueuse/core": { @@ -11033,6 +11761,13 @@ "node": ">=10.0.0" } }, + "node_modules/@zeit/schemas": { + "version": "2.36.0", + "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", + "dev": true, + "license": "MIT" + }, "node_modules/@zxing/text-encoding": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", @@ -11095,9 +11830,8 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "devOptional": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" @@ -11222,6 +11956,16 @@ "optional": true, "peer": true }, + "node_modules/ansi-align": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.1.0" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -11426,6 +12170,27 @@ "license": "ISC", "optional": true }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/are-we-there-yet": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", @@ -11967,9 +12732,9 @@ "optional": true }, "node_modules/bare-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.1.6.tgz", - "integrity": "sha512-25RsLF33BqooOEFNdMcEhMpJy8EoR88zSMrnOQOaM3USnOK2VmaJ1uaQEwPA6AQjrv1lXChScosN6CzbwbO9OQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.2.0.tgz", + "integrity": "sha512-oRfrw7gwwBVAWx9S5zPMo2iiOjxyiZE12DmblmMQREgcogbNO0AFaZ+QBxxkEXiPspcpvO/Qtqn8LabUx4uYXg==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -12013,9 +12778,9 @@ } }, "node_modules/bare-stream": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.5.tgz", - "integrity": "sha512-jSmxKJNJmHySi6hC42zlZnq00rga4jjxcgNZjY9N5WlOe/iOoGRtdwGsHzQv2RlH2KOYMwGUXhf2zXd32BA9RA==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -12222,54 +12987,201 @@ "dev": true, "license": "ISC" }, - "node_modules/bplist-creator": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", - "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", - "devOptional": true, + "node_modules/boxen": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", + "dev": true, "license": "MIT", "dependencies": { - "stream-buffers": "2.2.x" + "ansi-align": "^3.0.1", + "camelcase": "^7.0.0", + "chalk": "^5.0.1", + "cli-boxes": "^3.0.0", + "string-width": "^5.1.2", + "type-fest": "^2.13.0", + "widest-line": "^4.0.1", + "wrap-ansi": "^8.0.1" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/bplist-parser": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", - "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "node_modules/boxen/node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "dev": true, "license": "MIT", - "dependencies": { - "big-integer": "1.6.x" - }, "engines": { - "node": ">= 5.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "node_modules/boxen/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=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==", - "devOptional": true, + "node_modules/boxen/node_modules/camelcase": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", + "dev": true, "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" + "engines": { + "node": ">=14.16" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", + "node_modules/boxen/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/boxen/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/boxen/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/boxen/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/bplist-creator": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", + "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", "license": "MIT" }, "node_modules/browser-fs-access": { @@ -12413,9 +13325,9 @@ "license": "MIT" }, "node_modules/browserslist": { - "version": "4.25.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", - "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", + "version": "4.25.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz", + "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==", "devOptional": true, "funding": [ { @@ -12433,8 +13345,8 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001733", - "electron-to-chromium": "^1.5.199", + "caniuse-lite": "^1.0.30001735", + "electron-to-chromium": "^1.5.204", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, @@ -12923,9 +13835,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001734", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz", - "integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==", + "version": "1.0.30001735", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", + "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", "devOptional": true, "funding": [ { @@ -13008,6 +13920,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", @@ -13232,6 +14160,19 @@ "node": ">=6" } }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -13288,6 +14229,24 @@ "node": ">=8" } }, + "node_modules/clipboardy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arch": "^2.2.0", + "execa": "^5.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -13394,6 +14353,13 @@ "color-support": "bin.js" } }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "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", @@ -13447,9 +14413,8 @@ "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "devOptional": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { "mime-db": ">= 1.43.0 < 2" }, @@ -13723,6 +14688,16 @@ "license": "ISC", "optional": true }, + "node_modules/content-disposition": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/conventional-changelog": { "version": "3.1.25", "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", @@ -14084,14 +15059,6 @@ "node": ">=4" } }, - "node_modules/cosmiconfig/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true - }, "node_modules/crc": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", @@ -15305,9 +16272,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.199", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.199.tgz", - "integrity": "sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==", + "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==", "devOptional": true, "license": "ISC" }, @@ -15487,6 +16454,19 @@ "node": ">=6" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/err-code": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", @@ -15573,9 +16553,9 @@ } }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -15583,32 +16563,35 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/escalade": { @@ -16227,6 +17210,13 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -16707,6 +17697,23 @@ "license": "Apache-2.0", "optional": true }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", @@ -17233,6 +18240,19 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -17406,6 +18426,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/getenv": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/getenv/-/getenv-2.0.0.tgz", @@ -17557,6 +18590,26 @@ "node": "*" } }, + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -17943,12 +18996,28 @@ "ms": "^2.0.0" } }, - "node_modules/iconv-corefoundation": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", - "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, "license": "MIT", - "optional": true, + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "license": "MIT", + "optional": true, "os": [ "darwin" ], @@ -18137,14 +19206,10 @@ } }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } @@ -18425,6 +19490,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-port-reachable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -18601,9 +19679,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "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": { @@ -19483,12 +20561,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT" - }, "node_modules/jsc-safe-url": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", @@ -19590,9 +20662,9 @@ "license": "MIT" }, "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -20551,554 +21623,458 @@ "uc.micro": "^2.0.0" } }, - "node_modules/load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "node_modules/lint-staged": { + "version": "15.5.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.2.tgz", + "integrity": "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==", "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" + "chalk": "^5.4.1", + "commander": "^13.1.0", + "debug": "^4.4.0", + "execa": "^8.0.1", + "lilconfig": "^3.1.3", + "listr2": "^8.2.5", + "micromatch": "^4.0.8", + "pidtree": "^0.6.0", + "string-argv": "^0.3.2", + "yaml": "^2.7.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": ">=4" + "node": ">=18.12.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" } }, - "node_modules/load-json-file/node_modules/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", + "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==", "dev": true, "license": "MIT", - "dependencies": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - }, "engines": { - "node": ">=4" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/load-json-file/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "node_modules/lint-staged/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "node_modules/lint-staged/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", - "engines": { - "node": ">=4" - } - }, - "node_modules/localforage": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", - "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", - "license": "Apache-2.0", "dependencies": { - "lie": "3.1.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/localforage/node_modules/lie": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", - "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, "license": "MIT", "dependencies": { - "immediate": "~3.0.5" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/localstorage-slim": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/localstorage-slim/-/localstorage-slim-2.7.1.tgz", - "integrity": "sha512-ExqYZl+pRpxPIcx0n0541T5CmSdRulHJCPDLrxIe23d4ehbqERDNJHcwsAVrVP09CZ/0j3PU92sI8a3KaGqXPQ==", - "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==", - "devOptional": true, + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lockfile": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", - "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "license": "ISC", - "dependencies": { - "signal-exit": "^3.0.2" + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", - "license": "MIT" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "license": "MIT" + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/lodash.ismatch": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", - "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "node_modules/lint-staged/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/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==", + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, - "license": "MIT" - }, - "node_modules/lodash.throttle": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", - "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "path-key": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" + "mimic-fn": "^4.0.0" }, - "bin": { - "loose-envify": "cli.js" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/ltgt": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", - "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "license": "MIT" - }, - "node_modules/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", - "license": "MIT", + "license": "ISC", "engines": { - "node": ">=12" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "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==", + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "license": "ISC", + "node_modules/listr2": { + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.3.tgz", + "integrity": "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==", + "dev": true, + "license": "MIT", "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=18.0.0" } }, - "node_modules/make-fetch-happen/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==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "dev": true, "license": "MIT", - "dependencies": { - "debug": "4" - }, "engines": { - "node": ">= 6.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/make-fetch-happen/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==", + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, "engines": { - "node": ">= 6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/make-fetch-happen/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==", + "node_modules/listr2/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, "license": "MIT", "dependencies": { - "agent-base": "6", - "debug": "4" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "license": "ISC", - "engines": { - "node": ">=12" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "devOptional": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "node_modules/listr2/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "node_modules/listr2/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/markdownlint": { - "version": "0.37.4", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.37.4.tgz", - "integrity": "sha512-u00joA/syf3VhWh6/ybVFkib5Zpj2e5KB/cfCei8fkSRuums6nyisTWGqjTWIOFoFwuXoTBQQiqlB4qFKp8ncQ==", + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "markdown-it": "14.1.0", - "micromark": "4.0.1", - "micromark-core-commonmark": "2.0.2", - "micromark-extension-directive": "3.0.2", - "micromark-extension-gfm-autolink-literal": "2.1.0", - "micromark-extension-gfm-footnote": "2.1.0", - "micromark-extension-gfm-table": "2.1.0", - "micromark-extension-math": "3.1.0", - "micromark-util-types": "2.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/DavidAnson" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/markdownlint-cli": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.44.0.tgz", - "integrity": "sha512-ZJTAONlvF9NkrIBltCdW15DxN9UTbPiKMEqAh2EU2gwIFlrCMavyCEPPO121cqfYOrLUJWW8/XKWongstmmTeQ==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "license": "MIT", "dependencies": { - "commander": "~13.1.0", - "glob": "~10.4.5", - "ignore": "~7.0.3", - "js-yaml": "~4.1.0", - "jsonc-parser": "~3.3.1", - "jsonpointer": "~5.0.1", - "markdownlint": "~0.37.4", - "minimatch": "~9.0.5", - "run-con": "~1.3.2", - "smol-toml": "~1.3.1" - }, - "bin": { - "markdownlint": "markdownlint.js" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/markdownlint-cli/node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/markdownlint-cli/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=4" } }, - "node_modules/markdownlint-cli/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", "dev": true, "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, "engines": { - "node": ">= 4" + "node": ">=4" } }, - "node_modules/markdownlint-cli/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4" } }, - "node_modules/markdownlint-cli/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=4" } }, - "node_modules/marky": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", - "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "node_modules/localforage": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/localforage/-/localforage-1.10.0.tgz", + "integrity": "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg==", "license": "Apache-2.0", - "optional": true, - "peer": true - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "lie": "3.1.1" } }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "node_modules/localforage/node_modules/lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw==", "license": "MIT", "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "immediate": "~3.0.5" } }, - "node_modules/mdurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", - "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", - "dev": true, + "node_modules/localstorage-slim": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/localstorage-slim/-/localstorage-slim-2.7.1.tgz", + "integrity": "sha512-ExqYZl+pRpxPIcx0n0541T5CmSdRulHJCPDLrxIe23d4ehbqERDNJHcwsAVrVP09CZ/0j3PU92sI8a3KaGqXPQ==", "license": "MIT" }, - "node_modules/memoize-one": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", - "dev": true, + "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==", + "devOptional": true, "license": "MIT", "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" + "p-locate": "^5.0.0" }, "engines": { "node": ">=10" @@ -21107,427 +22083,436 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/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==", + "node_modules/lockfile": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", + "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "signal-exit": "^3.0.2" } }, - "node_modules/meow/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/meow/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==", + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "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/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "license": "MIT", "dependencies": { - "p-locate": "^4.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/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==", + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^2.0.0" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/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==", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "environment": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", "dev": true, "license": "MIT", - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/meow/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/log-update/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", + "get-east-asian-width": "^1.0.0" + }, "engines": { - "node": ">=8" - } - }, - "node_modules/meow/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/meow/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "node_modules/log-update/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, - "license": "(MIT OR CC0-1.0)", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "devOptional": true, + "node_modules/log-update/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, "engines": { - "node": ">= 8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mergexml": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/mergexml/-/mergexml-1.2.4.tgz", - "integrity": "sha512-yiOlDqcVCz7AG1eSboonc18FTlfqDEKYfGoAV3Lul98u6YRV/s0kjtf4bjk47t0hLTFJR0BSYMd6BpmX3xDjNQ==", + "node_modules/log-update/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, "license": "ISC", - "dependencies": { - "@xmldom/xmldom": "^0.7.0", - "formidable": "^3.5.1", - "xpath": "0.0.27" - } - }, - "node_modules/mergexml/node_modules/xpath": { - "version": "0.0.27", - "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz", - "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=0.6.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/merkletreejs": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.3.11.tgz", - "integrity": "sha512-LJKTl4iVNTndhL+3Uz/tfkjD0klIWsHlUzgtuNnNrsf7bAlXR30m+xYB7lHr5Z/l6e/yAIsr26Dabx6Buo4VGQ==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, "license": "MIT", "dependencies": { - "bignumber.js": "^9.0.1", - "buffer-reverse": "^1.0.1", - "crypto-js": "^4.2.0", - "treeify": "^1.1.0", - "web3-utils": "^1.3.4" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">= 7.6.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/meshoptimizer": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", - "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, - "license": "MIT" - }, - "node_modules/metro": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro/-/metro-0.82.5.tgz", - "integrity": "sha512-8oAXxL7do8QckID/WZEKaIFuQJFUTLzfVcC48ghkHhNK2RGuQq8Xvf4AVd+TUA0SZtX0q8TGNXZ/eba1ckeGCg==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "@babel/types": "^7.25.2", - "accepts": "^1.3.7", - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "connect": "^3.6.5", - "debug": "^4.4.0", - "error-stack-parser": "^2.0.6", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "hermes-parser": "0.29.1", - "image-size": "^1.0.2", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "jsc-safe-url": "^0.2.2", - "lodash.throttle": "^4.1.1", - "metro-babel-transformer": "0.82.5", - "metro-cache": "0.82.5", - "metro-cache-key": "0.82.5", - "metro-config": "0.82.5", - "metro-core": "0.82.5", - "metro-file-map": "0.82.5", - "metro-resolver": "0.82.5", - "metro-runtime": "0.82.5", - "metro-source-map": "0.82.5", - "metro-symbolicate": "0.82.5", - "metro-transform-plugins": "0.82.5", - "metro-transform-worker": "0.82.5", - "mime-types": "^2.1.27", - "nullthrows": "^1.1.1", - "serialize-error": "^2.1.0", - "source-map": "^0.5.6", - "throat": "^5.0.0", - "ws": "^7.5.10", - "yargs": "^17.6.2" - }, - "bin": { - "metro": "src/cli.js" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=18.18" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/metro-babel-transformer": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.82.5.tgz", - "integrity": "sha512-W/scFDnwJXSccJYnOFdGiYr9srhbHPdxX9TvvACOFsIXdLilh3XuxQl/wXW6jEJfgIb0jTvoTlwwrqvuwymr6Q==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/core": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "hermes-parser": "0.29.1", - "nullthrows": "^1.1.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=18.18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/metro-babel-transformer/node_modules/hermes-estree": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", - "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro-babel-transformer/node_modules/hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", - "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "hermes-estree": "0.29.1" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/metro-cache": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.82.5.tgz", - "integrity": "sha512-AwHV9607xZpedu1NQcjUkua8v7HfOTKfftl6Vc9OGr/jbpiJX6Gpy8E/V9jo/U9UuVYX2PqSUcVNZmu+LTm71Q==", + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { - "exponential-backoff": "^3.1.1", - "flow-enums-runtime": "^0.0.6", - "https-proxy-agent": "^7.0.5", - "metro-core": "0.82.5" + "js-tokens": "^3.0.0 || ^4.0.0" }, - "engines": { - "node": ">=18.18" + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/metro-cache-key": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.82.5.tgz", - "integrity": "sha512-qpVmPbDJuRLrT4kcGlUouyqLGssJnbTllVtvIgXfR7ZuzMKf0mGS+8WzcqzNK8+kCyakombQWR0uDd8qhWGJcA==", + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "flow-enums-runtime": "^0.0.6" - }, "engines": { - "node": ">=18.18" + "node": ">=8" } }, - "node_modules/metro-config": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.82.5.tgz", - "integrity": "sha512-/r83VqE55l0WsBf8IhNmc/3z71y2zIPe5kRSuqA5tY/SL/ULzlHUJEMd1szztd0G45JozLwjvrhAzhDPJ/Qo/g==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "connect": "^3.6.5", - "cosmiconfig": "^5.0.5", - "flow-enums-runtime": "^0.0.6", - "jest-validate": "^29.7.0", - "metro": "0.82.5", - "metro-cache": "0.82.5", - "metro-core": "0.82.5", - "metro-runtime": "0.82.5" - }, - "engines": { - "node": ">=18.18" - } + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" }, - "node_modules/metro-config/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==", + "node_modules/ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", + "dev": true, + "license": "MIT" + }, + "node_modules/luxon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/metro-config/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==", + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "license": "MIT", - "optional": true, - "peer": true, "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" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/metro-config/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==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro-config/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==", + "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", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" + "dependencies": { + "semver": "^7.5.3" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/metro-config/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "license": "MIT", - "optional": true, - "peer": true, "engines": { "node": ">=10" }, @@ -21535,604 +22520,341 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/metro-config/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==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "license": "ISC", "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" + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/metro-config/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==", + "node_modules/make-fetch-happen/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==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "debug": "4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 6.0.0" } }, - "node_modules/metro-core": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.82.5.tgz", - "integrity": "sha512-OJL18VbSw2RgtBm1f2P3J5kb892LCVJqMvslXxuxjAPex8OH7Eb8RBfgEo7VZSjgb/LOf4jhC4UFk5l5tAOHHA==", + "node_modules/make-fetch-happen/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==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "flow-enums-runtime": "^0.0.6", - "lodash.throttle": "^4.1.1", - "metro-resolver": "0.82.5" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=18.18" + "node": ">= 6" } }, - "node_modules/metro-file-map": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.82.5.tgz", - "integrity": "sha512-vpMDxkGIB+MTN8Af5hvSAanc6zXQipsAUO+XUx3PCQieKUfLwdoa8qaZ1WAQYRpaU+CJ8vhBcxtzzo3d9IsCIQ==", + "node_modules/make-fetch-happen/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==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "debug": "^4.4.0", - "fb-watchman": "^2.0.0", - "flow-enums-runtime": "^0.0.6", - "graceful-fs": "^4.2.4", - "invariant": "^2.2.4", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "nullthrows": "^1.1.1", - "walker": "^1.0.7" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=18.18" + "node": ">= 6" } }, - "node_modules/metro-file-map/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==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/metro-file-map/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==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "devOptional": true, + "license": "BSD-3-Clause", "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" + "tmpl": "1.0.5" } }, - "node_modules/metro-file-map/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==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro-file-map/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" + "node": ">=8" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/metro-file-map/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==", + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "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" + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/metro-file-map/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==", + "node_modules/markdownlint": { + "version": "0.37.4", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.37.4.tgz", + "integrity": "sha512-u00joA/syf3VhWh6/ybVFkib5Zpj2e5KB/cfCei8fkSRuums6nyisTWGqjTWIOFoFwuXoTBQQiqlB4qFKp8ncQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "markdown-it": "14.1.0", + "micromark": "4.0.1", + "micromark-core-commonmark": "2.0.2", + "micromark-extension-directive": "3.0.2", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.0", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/metro-file-map/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro-file-map/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.6" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/DavidAnson" } }, - "node_modules/metro-file-map/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==", + "node_modules/markdownlint-cli": { + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.44.0.tgz", + "integrity": "sha512-ZJTAONlvF9NkrIBltCdW15DxN9UTbPiKMEqAh2EU2gwIFlrCMavyCEPPO121cqfYOrLUJWW8/XKWongstmmTeQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "has-flag": "^4.0.0" + "commander": "~13.1.0", + "glob": "~10.4.5", + "ignore": "~7.0.3", + "js-yaml": "~4.1.0", + "jsonc-parser": "~3.3.1", + "jsonpointer": "~5.0.1", + "markdownlint": "~0.37.4", + "minimatch": "~9.0.5", + "run-con": "~1.3.2", + "smol-toml": "~1.3.1" }, - "engines": { - "node": ">=10" + "bin": { + "markdownlint": "markdownlint.js" }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "engines": { + "node": ">=18" } }, - "node_modules/metro-minify-terser": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.82.5.tgz", - "integrity": "sha512-v6Nx7A4We6PqPu/ta1oGTqJ4Usz0P7c+3XNeBxW9kp8zayS3lHUKR0sY0wsCHInxZlNAEICx791x+uXytFUuwg==", + "node_modules/markdownlint-cli/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "flow-enums-runtime": "^0.0.6", - "terser": "^5.15.0" - }, "engines": { - "node": ">=18.18" + "node": ">=18" } }, - "node_modules/metro-resolver": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.82.5.tgz", - "integrity": "sha512-kFowLnWACt3bEsuVsaRNgwplT8U7kETnaFHaZePlARz4Fg8tZtmRDUmjaD68CGAwc0rwdwNCkWizLYpnyVcs2g==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/markdownlint-cli/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", "dependencies": { - "flow-enums-runtime": "^0.0.6" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=18.18" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/metro-runtime": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.82.5.tgz", - "integrity": "sha512-rQZDoCUf7k4Broyw3Ixxlq5ieIPiR1ULONdpcYpbJQ6yQ5GGEyYjtkztGD+OhHlw81LCR2SUAoPvtTus2WDK5g==", + "node_modules/markdownlint-cli/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/runtime": "^7.25.0", - "flow-enums-runtime": "^0.0.6" - }, "engines": { - "node": ">=18.18" + "node": ">= 4" } }, - "node_modules/metro-source-map": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.82.5.tgz", - "integrity": "sha512-wH+awTOQJVkbhn2SKyaw+0cd+RVSCZ3sHVgyqJFQXIee/yLs3dZqKjjeKKhhVeudgjXo7aE/vSu/zVfcQEcUfw==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/markdownlint-cli/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", "dependencies": { - "@babel/traverse": "^7.25.3", - "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", - "@babel/types": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-symbolicate": "0.82.5", - "nullthrows": "^1.1.1", - "ob1": "0.82.5", - "source-map": "^0.5.6", - "vlq": "^1.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=18.18" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/metro-source-map/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true, + "node_modules/markdownlint-cli/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/metro-symbolicate": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.82.5.tgz", - "integrity": "sha512-1u+07gzrvYDJ/oNXuOG1EXSvXZka/0JSW1q2EYBWerVKMOhvv9JzDGyzmuV7hHbF2Hg3T3S2uiM36sLz1qKsiw==", + "node_modules/markdownlint-cli2": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.18.1.tgz", + "integrity": "sha512-/4Osri9QFGCZOCTkfA8qJF+XGjKYERSHkXzxSyS1hd3ZERJGjvsUao2h4wdnvpHp6Tu2Jh/bPHM0FE9JJza6ng==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "flow-enums-runtime": "^0.0.6", - "invariant": "^2.2.4", - "metro-source-map": "0.82.5", - "nullthrows": "^1.1.1", - "source-map": "^0.5.6", - "vlq": "^1.0.0" + "globby": "14.1.0", + "js-yaml": "4.1.0", + "jsonc-parser": "3.3.1", + "markdown-it": "14.1.0", + "markdownlint": "0.38.0", + "markdownlint-cli2-formatter-default": "0.0.5", + "micromatch": "4.0.8" }, "bin": { - "metro-symbolicate": "src/index.js" + "markdownlint-cli2": "markdownlint-cli2-bin.mjs" }, "engines": { - "node": ">=18.18" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/DavidAnson" } }, - "node_modules/metro-symbolicate/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" + "node_modules/markdownlint-cli2-formatter-default": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.5.tgz", + "integrity": "sha512-4XKTwQ5m1+Txo2kuQ3Jgpo/KmnG+X90dWt4acufg6HVGadTUG5hzHF/wssp9b5MBYOMCnZ9RMPaU//uHsszF8Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/DavidAnson" + }, + "peerDependencies": { + "markdownlint-cli2": ">=0.0.4" } }, - "node_modules/metro-transform-plugins": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.82.5.tgz", - "integrity": "sha512-57Bqf3rgq9nPqLrT2d9kf/2WVieTFqsQ6qWHpEng5naIUtc/Iiw9+0bfLLWSAw0GH40iJ4yMjFcFJDtNSYynMA==", + "node_modules/markdownlint-cli2/node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/template": "^7.25.0", - "@babel/traverse": "^7.25.3", - "flow-enums-runtime": "^0.0.6", - "nullthrows": "^1.1.1" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" }, "engines": { - "node": ">=18.18" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/metro-transform-worker": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.82.5.tgz", - "integrity": "sha512-mx0grhAX7xe+XUQH6qoHHlWedI8fhSpDGsfga7CpkO9Lk9W+aPitNtJWNGrW8PfjKEWbT9Uz9O50dkI8bJqigw==", + "node_modules/markdownlint-cli2/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "@babel/core": "^7.25.2", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", - "@babel/types": "^7.25.2", - "flow-enums-runtime": "^0.0.6", - "metro": "0.82.5", - "metro-babel-transformer": "0.82.5", - "metro-cache": "0.82.5", - "metro-cache-key": "0.82.5", - "metro-minify-terser": "0.82.5", - "metro-source-map": "0.82.5", - "metro-transform-plugins": "0.82.5", - "nullthrows": "^1.1.1" - }, "engines": { - "node": ">=18.18" + "node": ">= 4" } }, - "node_modules/metro/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==", + "node_modules/markdownlint-cli2/node_modules/markdownlint": { + "version": "0.38.0", + "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.38.0.tgz", + "integrity": "sha512-xaSxkaU7wY/0852zGApM8LdlIfGCW8ETZ0Rr62IQtAnUMlMuifsg09vWJcNYeL4f0anvr8Vo4ZQar8jGpV0btQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/metro/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==", - "license": "MIT", - "optional": true, - "peer": true, - "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/metro/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==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro/node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/metro/node_modules/hermes-estree": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", - "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro/node_modules/hermes-parser": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", - "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "hermes-estree": "0.29.1" - } - }, - "node_modules/metro/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==", - "license": "MIT", - "optional": true, - "peer": true, - "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/metro/node_modules/jest-util/node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/metro/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==", - "license": "MIT", - "optional": true, - "peer": true, - "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/metro/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/metro/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/metro/node_modules/serialize-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", - "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/metro/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/metro/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==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "has-flag": "^4.0.0" + "micromark": "4.0.2", + "micromark-core-commonmark": "2.0.3", + "micromark-extension-directive": "4.0.0", + "micromark-extension-gfm-autolink-literal": "2.1.0", + "micromark-extension-gfm-footnote": "2.1.0", + "micromark-extension-gfm-table": "2.1.1", + "micromark-extension-math": "3.1.0", + "micromark-util-types": "2.0.2" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/metro/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "url": "https://github.com/sponsors/DavidAnson" } }, - "node_modules/micro-ftch": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", - "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", - "license": "MIT" - }, - "node_modules/micromark": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", - "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "node_modules/markdownlint-cli2/node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", "dev": true, "funding": [ { @@ -22165,10 +22887,10 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", - "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "node_modules/markdownlint-cli2/node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", "dev": true, "funding": [ { @@ -22200,10 +22922,10 @@ "micromark-util-types": "^2.0.0" } }, - "node_modules/micromark-extension-directive": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", - "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "node_modules/markdownlint-cli2/node_modules/micromark-extension-directive": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-4.0.0.tgz", + "integrity": "sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==", "dev": true, "license": "MIT", "dependencies": { @@ -22220,15 +22942,16 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "node_modules/markdownlint-cli2/node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", "dev": true, "license": "MIT", "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" }, @@ -22237,1383 +22960,2637 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "node_modules/markdownlint-cli2/node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", "dev": true, - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/markdownlint-cli2/node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", - "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "node_modules/markdownlint-cli2/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "engines": { + "node": ">=14.16" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark-extension-math": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", - "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "license": "MIT", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, "license": "MIT", "dependencies": { - "@types/katex": "^0.16.0", - "devlop": "^1.0.0", - "katex": "^0.16.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "node_modules/meow/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, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "node_modules/meow/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/meow/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, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "node_modules/meow/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, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "node_modules/meow/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, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "node_modules/meow/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "node_modules/meow/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" } }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" } }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "node_modules/meow/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "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==", + "devOptional": true, "license": "MIT" }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "devOptional": true, "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" + "engines": { + "node": ">= 8" } }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "node_modules/mergexml": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/mergexml/-/mergexml-1.2.4.tgz", + "integrity": "sha512-yiOlDqcVCz7AG1eSboonc18FTlfqDEKYfGoAV3Lul98u6YRV/s0kjtf4bjk47t0hLTFJR0BSYMd6BpmX3xDjNQ==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", + "license": "ISC", "dependencies": { - "micromark-util-types": "^2.0.0" + "@xmldom/xmldom": "^0.7.0", + "formidable": "^3.5.1", + "xpath": "0.0.27" } }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "node_modules/mergexml/node_modules/xpath": { + "version": "0.0.27", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz", + "integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" + "engines": { + "node": ">=0.6.0" } }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], + "node_modules/merkletreejs": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/merkletreejs/-/merkletreejs-0.3.11.tgz", + "integrity": "sha512-LJKTl4iVNTndhL+3Uz/tfkjD0klIWsHlUzgtuNnNrsf7bAlXR30m+xYB7lHr5Z/l6e/yAIsr26Dabx6Buo4VGQ==", "license": "MIT", "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" + "bignumber.js": "^9.0.1", + "buffer-reverse": "^1.0.1", + "crypto-js": "^4.2.0", + "treeify": "^1.1.0", + "web3-utils": "^1.3.4" + }, + "engines": { + "node": ">= 7.6.0" } }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", - "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], "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==", - "devOptional": true, + "node_modules/metro": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.1.tgz", + "integrity": "sha512-UGKepmTxoGD4HkQV8YWvpvwef7fUujNtTgG4Ygf7m/M0qjvb9VuDmAsEU+UdriRX7F61pnVK/opz89hjKlYTXA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" + "@babel/code-frame": "^7.24.7", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.3", + "@babel/types": "^7.25.2", + "accepts": "^1.3.7", + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.29.1", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.83.1", + "metro-cache": "0.83.1", + "metro-cache-key": "0.83.1", + "metro-config": "0.83.1", + "metro-core": "0.83.1", + "metro-file-map": "0.83.1", + "metro-resolver": "0.83.1", + "metro-runtime": "0.83.1", + "metro-source-map": "0.83.1", + "metro-symbolicate": "0.83.1", + "metro-transform-plugins": "0.83.1", + "metro-transform-worker": "0.83.1", + "mime-types": "^2.1.27", + "nullthrows": "^1.1.1", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "throat": "^5.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" }, "engines": { - "node": ">=8.6" + "node": ">=20.19.4" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, + "node_modules/metro-babel-transformer": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.1.tgz", + "integrity": "sha512-r3xAD3964E8dwDBaZNSO2aIIvWXjIK80uO2xo0/pi3WI8XWT9h5SCjtGWtMtE5PRWw+t20TN0q1WMRsjvhC1rQ==", "license": "MIT", - "engines": { - "node": ">=8.6" + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.29.1", + "nullthrows": "^1.1.1" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "engines": { + "node": ">=20.19.4" } }, - "node_modules/miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, + "node_modules/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "bin": { - "miller-rabin": "bin/miller-rabin" + "hermes-estree": "0.29.1" } }, - "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/metro-cache": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.1.tgz", + "integrity": "sha512-7N/Ad1PHa1YMWDNiyynTPq34Op2qIE68NWryGEQ4TSE3Zy6a8GpsYnEEZE4Qi6aHgsE+yZHKkRczeBgxhnFIxQ==", "license": "MIT", - "bin": { - "mime": "cli.js" + "optional": true, + "peer": true, + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.83.1" }, "engines": { - "node": ">=4.0.0" + "node": ">=20.19.4" } }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "node_modules/metro-cache-key": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.1.tgz", + "integrity": "sha512-ZUs+GD5CNeDLxx5UUWmfg26IL+Dnbryd+TLqTlZnDEgehkIa11kUSvgF92OFfJhONeXzV4rZDRGNXoo6JT+8Gg==", "license": "MIT", "optional": true, "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, "engines": { - "node": ">= 0.6" + "node": ">=20.19.4" } }, - "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==", + "node_modules/metro-config": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.1.tgz", + "integrity": "sha512-HJhpZx3wyOkux/jeF1o7akFJzZFdbn6Zf7UQqWrvp7gqFqNulQ8Mju09raBgPmmSxKDl4LbbNeigkX0/nKY1QA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "mime-db": "1.52.0" + "connect": "^3.6.5", + "cosmiconfig": "^5.0.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.83.1", + "metro-cache": "0.83.1", + "metro-core": "0.83.1", + "metro-runtime": "0.83.1" }, "engines": { - "node": ">= 0.6" + "node": ">=20.19.4" } }, - "node_modules/mime-types/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==", + "node_modules/metro-config/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==", "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, "engines": { - "node": ">= 0.6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "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==", + "node_modules/metro-config/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==", "license": "MIT", + "optional": true, + "peer": true, + "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": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "node_modules/metro-config/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==", "license": "MIT", - "engines": { - "node": ">=4" - } + "optional": true, + "peer": true }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, + "node_modules/metro-config/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==", "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "license": "MIT" - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, + "node_modules/metro-config/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, + "node_modules/metro-config/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==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "@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": ">= 6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", + "node_modules/metro-config/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==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "yallist": "^4.0.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "license": "ISC", + "node_modules/metro-core": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.1.tgz", + "integrity": "sha512-uVL1eAJcMFd2o2Q7dsbpg8COaxjZBBGaXqO2OHnivpCdfanraVL8dPmY6It9ZeqWLOihUKZ2yHW4b6soVCzH/Q==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "minipass": "^3.0.0" + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.83.1" }, "engines": { - "node": ">= 8" + "node": ">=20.19.4" } }, - "node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "node_modules/metro-file-map": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.1.tgz", + "integrity": "sha512-Yu429lnexKl44PttKw3nhqgmpBR+6UQ/tRaYcxPeEShtcza9DWakCn7cjqDTQZtWR2A8xSNv139izJMyQ4CG+w==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "debug": "^4.4.0", + "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" + "node": ">=20.19.4" } }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "license": "ISC", + "node_modules/metro-file-map/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==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "minipass": "^3.0.0" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">= 8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", + "node_modules/metro-file-map/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==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "minipass": "^3.0.0" + "@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": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "license": "ISC", + "node_modules/metro-file-map/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==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/metro-file-map/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "minipass": "^3.0.0" + "ms": "^2.1.3" }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/metro-file-map/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==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "@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": ">= 8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "license": "MIT" - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/metro-file-map/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==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "minimist": "^1.2.6" + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" + "node_modules/metro-file-map/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true }, - "node_modules/modify-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", - "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", - "dev": true, + "node_modules/metro-file-map/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, - "node_modules/msrcrypto": { - "version": "1.5.8", - "resolved": "https://registry.npmjs.org/msrcrypto/-/msrcrypto-1.5.8.tgz", - "integrity": "sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q==", - "license": "Apache-2.0", - "optional": true - }, - "node_modules/multibase": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-4.0.6.tgz", - "integrity": "sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ==", - "deprecated": "This module has been superseded by the multiformats module", + "node_modules/metro-file-map/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==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@multiformats/base-x": "^4.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=12.0.0", - "npm": ">=6.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/multiformats": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-12.1.3.tgz", - "integrity": "sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==", - "license": "Apache-2.0 OR MIT", + "node_modules/metro-minify-terser": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.1.tgz", + "integrity": "sha512-kmooOxXLvKVxkh80IVSYO4weBdJDhCpg5NSPkjzzAnPJP43u6+usGXobkTWxxrAlq900bhzqKek4pBsUchlX6A==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, "engines": { - "node": ">=16.0.0", - "npm": ">=7.0.0" + "node": ">=20.19.4" } }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "devOptional": true, + "node_modules/metro-resolver": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.1.tgz", + "integrity": "sha512-t8j46kiILAqqFS5RNa+xpQyVjULxRxlvMidqUswPEk5nQVNdlJslqizDm/Et3v/JKwOtQGkYAQCHxP1zGStR/g==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" } }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/metro-runtime": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.1.tgz", + "integrity": "sha512-3Ag8ZS4IwafL/JUKlaeM6/CbkooY+WcVeqdNlBG0m4S0Qz0om3rdFdy1y6fYBpl6AwXJwWeMuXrvZdMuByTcRA==", "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "optional": true, + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">=20.19.4" } }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/napi-postinstall": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", - "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", - "dev": true, + "node_modules/metro-source-map": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.1.tgz", + "integrity": "sha512-De7Vbeo96fFZ2cqmI0fWwVJbtHIwPZv++LYlWSwzTiCzxBDJORncN0LcT48Vi2UlQLzXJg+/CuTAcy7NBVh69A==", "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" + "optional": true, + "peer": true, + "dependencies": { + "@babel/traverse": "^7.25.3", + "@babel/traverse--for-generate-function-map": "npm:@babel/traverse@^7.25.3", + "@babel/types": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.83.1", + "nullthrows": "^1.1.1", + "ob1": "0.83.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" }, "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" + "node": ">=20.19.4" } }, - "node_modules/native-run": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.1.tgz", - "integrity": "sha512-XfG1FBZLM50J10xH9361whJRC9SHZ0Bub4iNRhhI61C8Jv0e1ud19muex6sNKB51ibQNUJNuYn25MuYET/rE6w==", + "node_modules/metro-source-map/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.1.tgz", + "integrity": "sha512-wPxYkONlq/Sv8Ji7vHEx5OzFouXAMQJjpcPW41ySKMLP/Ir18SsiJK2h4YkdKpYrTS1+0xf8oqF6nxCsT3uWtg==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@ionic/utils-fs": "^3.1.7", - "@ionic/utils-terminal": "^2.3.4", - "bplist-parser": "^0.3.2", - "debug": "^4.3.4", - "elementtree": "^0.1.7", - "ini": "^4.1.1", - "plist": "^3.1.0", - "split2": "^4.2.0", - "through2": "^4.0.2", - "tslib": "^2.6.2", - "yauzl": "^2.10.0" + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.83.1", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" }, "bin": { - "native-run": "bin/native-run" + "metro-symbolicate": "src/index.js" }, "engines": { - "node": ">=16.0.0" + "node": ">=20.19.4" } }, - "node_modules/native-run/node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "license": "ISC", + "node_modules/metro-symbolicate/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/native-run/node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", + "node_modules/metro-transform-plugins": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.1.tgz", + "integrity": "sha512-1Y+I8oozXwhuS0qwC+ezaHXBf0jXW4oeYn4X39XWbZt9X2HfjodqY9bH9r6RUTsoiK7S4j8Ni2C91bUC+sktJQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.3", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, "engines": { - "node": ">= 10.x" + "node": ">=20.19.4" } }, - "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/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "node_modules/metro-transform-worker": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.1.tgz", + "integrity": "sha512-owCrhPyUxdLgXEEEAL2b14GWTPZ2zYuab1VQXcfEy0sJE71iciD7fuMcrngoufh7e7UHDZ56q4ktXg8wgiYA1Q==", "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/types": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "metro": "0.83.1", + "metro-babel-transformer": "0.83.1", + "metro-cache": "0.83.1", + "metro-cache-key": "0.83.1", + "metro-minify-terser": "0.83.1", + "metro-source-map": "0.83.1", + "metro-transform-plugins": "0.83.1", + "nullthrows": "^1.1.1" + }, "engines": { - "node": ">= 0.6" + "node": ">=20.19.4" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nested-error-stacks": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", - "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", + "node_modules/metro/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==", "license": "MIT", "optional": true, - "peer": true - }, - "node_modules/node-abi": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", - "license": "MIT", + "peer": true, "dependencies": { - "semver": "^7.3.5" + "@sinclair/typebox": "^0.27.8" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/node-addon-api": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", - "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", - "license": "MIT", - "optional": true - }, - "node_modules/node-api-version": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", - "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "node_modules/metro/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==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "semver": "^7.3.5" + "@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/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], + "node_modules/metro/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==", "license": "MIT", "optional": true, - "engines": { - "node": ">=10.5.0" - } + "peer": true }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "node_modules/metro/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/metro/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "whatwg-url": "^5.0.0" + "ms": "^2.1.3" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": ">=6.0" }, "peerDependenciesMeta": { - "encoding": { + "supports-color": { "optional": true } } }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "license": "(BSD-3-Clause OR GPL-2.0)", + "node_modules/metro/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT", "optional": true, - "peer": true, - "engines": { - "node": ">= 6.13.0" - } + "peer": true }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "node_modules/metro/node_modules/hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" + "hermes-estree": "0.29.1" } }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", - "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "node_modules/metro/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==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { - "detect-libc": "^2.0.1" + "@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" }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/node-gyp/node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "license": "ISC", + "node_modules/metro/node_modules/jest-util/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" + "peer": true, + "engines": { + "node": ">=8" } }, - "node_modules/node-gyp/node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", + "node_modules/metro/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==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/node-gyp/node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/metro/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/metro/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "license": "MIT", "optional": true, + "peer": true, "engines": { - "node": ">= 6" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/node-gyp/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==", + "node_modules/metro/node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", "license": "MIT", "optional": true, - "dependencies": { - "debug": "4" - }, + "peer": true, "engines": { - "node": ">= 6.0.0" + "node": ">=0.10.0" } }, - "node_modules/node-gyp/node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "license": "ISC", + "node_modules/metro/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "optional": true, - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, + "peer": true, "engines": { - "node": ">= 10" + "node": ">=0.10.0" } }, - "node_modules/node-gyp/node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "node_modules/metro/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==", "license": "MIT", "optional": true, + "peer": true, "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/node-gyp/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==", + "node_modules/metro/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "license": "MIT", "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, + "peer": true, "engines": { - "node": ">= 6" + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/node-gyp/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "optional": true, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "license": "MIT" + }, + "node_modules/micromark": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.1.tgz", + "integrity": "sha512-eBPdkcoCNvYcxQOAKAlceo5SNdzZWfF+FcSupREAzdAh9rRmE239CEQAiTwIgblwnoM8zzj35sZ5ZwvSEOF6Kw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/node-gyp/node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "license": "ISC", - "optional": true, + "node_modules/micromark-core-commonmark": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.2.tgz", + "integrity": "sha512-FKjQKbxd1cibWMM1P9N+H8TwlgGgSkWZMmfuVucLCHaYqeSvJ0hFeHsIa65pA2nYbes0f8LDHPMrd9X7Ujxg9w==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/node-gyp/node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "node_modules/micromark-extension-directive": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/micromark-extension-directive/-/micromark-extension-directive-3.0.2.tgz", + "integrity": "sha512-wjcXHgk+PPdmvR58Le9d7zQYWy+vKEU9Se44p2CrCDPiLr2FMyiT4Fyb5UFKFC66wGB3kPlgD7q3TnoqPS7SZA==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "parse-entities": "^4.0.0" }, - "optionalDependencies": { - "encoding": "^0.1.12" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/node-gyp/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dev": true, "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, - "engines": { - "node": ">=10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "optional": true, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dev": true, + "license": "MIT", "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, - "engines": { - "node": ">=6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/node-gyp/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", - "license": "ISC", - "optional": true, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", + "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "dev": true, + "license": "MIT", "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/node-gyp/node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, - "engines": { - "node": ">= 10" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/node-gyp/node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "license": "ISC", - "optional": true, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/node-gyp/node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "license": "ISC", - "optional": true, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "unique-slug": "^2.0.0" + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/node-gyp/node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "license": "ISC", - "optional": true, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "imurmurhash": "^0.1.4" + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/node-html-parser": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.4.2.tgz", - "integrity": "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==", + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", "dependencies": { - "css-select": "^4.2.1", - "he": "1.2.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "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==", - "devOptional": 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==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", - "license": "ISC", + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", "dev": true, - "license": "BSD-2-Clause", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, - "engines": { - "node": ">=10" + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "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==", - "devOptional": true, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/nostr-wasm": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz", - "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==", + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.1.tgz", + "integrity": "sha512-534m2WhVTddrcKVepwmVEVnUAmtrx9bfIjNoQHRqfnvdaHQiFytEhJoTgpWJvDEXCO5gLTQh3wYC1PgOJA4NSQ==", + "dev": true, + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "devOptional": 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==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/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==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", + "license": "MIT" + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/msrcrypto": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/msrcrypto/-/msrcrypto-1.5.8.tgz", + "integrity": "sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q==", + "license": "Apache-2.0", + "optional": true + }, + "node_modules/multibase": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-4.0.6.tgz", + "integrity": "sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ==", + "deprecated": "This module has been superseded by the multiformats module", + "license": "MIT", + "dependencies": { + "@multiformats/base-x": "^4.0.1" + }, + "engines": { + "node": ">=12.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multiformats": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-12.1.3.tgz", + "integrity": "sha512-eajQ/ZH7qXZQR2AgtfpmSMizQzmyYVmCql7pdhldPuYQi4atACekbJaQplk6dWyIi10jCaFnd6pqvcEFXjbaJw==", + "license": "Apache-2.0 OR MIT", + "engines": { + "node": ">=16.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", + "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/native-run": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.1.tgz", + "integrity": "sha512-XfG1FBZLM50J10xH9361whJRC9SHZ0Bub4iNRhhI61C8Jv0e1ud19muex6sNKB51ibQNUJNuYn25MuYET/rE6w==", + "license": "MIT", + "dependencies": { + "@ionic/utils-fs": "^3.1.7", + "@ionic/utils-terminal": "^2.3.4", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^4.1.1", + "plist": "^3.1.0", + "split2": "^4.2.0", + "through2": "^4.0.2", + "tslib": "^2.6.2", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/native-run/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/native-run/node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "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/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nested-error-stacks": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.0.1.tgz", + "integrity": "sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "license": "MIT", + "optional": true + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "license": "(BSD-3-Clause OR GPL-2.0)", + "optional": true, + "peer": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/node-gyp/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==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/node-gyp/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-gyp/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/node-gyp/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==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/node-gyp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/node-gyp/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/node-gyp/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", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-gyp/node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-gyp/node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/node-gyp/node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/node-html-parser": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.4.2.tgz", + "integrity": "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^4.2.1", + "he": "1.2.0" + } + }, + "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==", + "devOptional": 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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nostr-wasm": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/nostr-wasm/-/nostr-wasm-0.1.0.tgz", + "integrity": "sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA==", "license": "MIT" }, "node_modules/notiwind": { @@ -23622,206 +25599,534 @@ "integrity": "sha512-u9owDTvjWtvf1DGdHB1/pVPBK47R1yeS0NAJfWPzwJTHk+JyyLby97fOkB63L1PRfbHkbUQD6BLEiPLzG3mwbQ==", "license": "MIT", "dependencies": { - "mitt": "^3.0.1" + "mitt": "^3.0.1" + }, + "peerDependencies": { + "vue": "^3.5.13" + } + }, + "node_modules/npm-check-updates": { + "version": "17.1.18", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.18.tgz", + "integrity": "sha512-bkUy2g4v1i+3FeUf5fXMLbxmV95eG4/sS7lYE32GrUeVgQRfQEk39gpskksFunyaxQgTIdrvYbnuNbO/pSUSqw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "ncu": "build/cli.js", + "npm-check-updates": "build/cli.js" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0", + "npm": ">=8.12.1" + } + }, + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg/node_modules/proc-log": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", + "license": "ISC", + "optional": true, + "peer": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.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/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "license": "MIT", + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "license": "MIT" + }, + "node_modules/ob1": { + "version": "0.83.1", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.1.tgz", + "integrity": "sha512-ngwqewtdUzFyycomdbdIhFLjePPSOt1awKMUXQ0L7iLHgWEPF3DsCerblzjzfAUHaXuvE9ccJymWQ/4PNNqvnQ==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/octal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/octal/-/octal-1.0.0.tgz", + "integrity": "sha512-nnda7W8d+A3vEIY+UrDQzzboPf1vhs4JYVhff5CDkq9QNoZY7Xrxeo/htox37j9dZf7yNHevZzqtejWgy1vCqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" }, - "peerDependencies": { - "vue": "^3.5.13" + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-check-updates": { - "version": "17.1.18", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.18.tgz", - "integrity": "sha512-bkUy2g4v1i+3FeUf5fXMLbxmV95eG4/sS7lYE32GrUeVgQRfQEk39gpskksFunyaxQgTIdrvYbnuNbO/pSUSqw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "ncu": "build/cli.js", - "npm-check-updates": "build/cli.js" + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0", - "npm": ">=8.12.1" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-package-arg": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", - "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", - "license": "ISC", - "optional": true, - "peer": true, + "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": { - "hosted-git-info": "^7.0.0", - "proc-log": "^4.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "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": "^16.14.0 || >=18.0.0" + "node": ">= 0.8.0" } }, - "node_modules/npm-package-arg/node_modules/hosted-git-info": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", - "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", - "license": "ISC", - "optional": true, - "peer": true, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "license": "MIT", "dependencies": { - "lru-cache": "^10.0.1" + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/npm-package-arg/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "license": "ISC", - "optional": true, - "peer": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node_modules/ora/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.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, + "node_modules/ora/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, + "node_modules/ora/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">= 6" } }, - "node_modules/nth-check": { + "node_modules/p-cancelable": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", "dependencies": { - "boolbase": "^1.0.0" + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "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==", + "devOptional": true, "license": "MIT", - "optional": true, - "peer": true + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/number-to-bn": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", - "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "license": "MIT", "dependencies": { - "bn.js": "4.11.6", - "strip-hex-prefix": "1.0.0" + "aggregate-error": "^3.0.0" }, "engines": { - "node": ">=6.5.0", - "npm": ">=3" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/number-to-bn/node_modules/bn.js": { - "version": "4.11.6", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", - "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "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==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "license": "(MIT AND Zlib)", + "optional": true + }, + "node_modules/papaparse": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", + "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", "license": "MIT" }, - "node_modules/ob1": { - "version": "0.82.5", - "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.82.5.tgz", - "integrity": "sha512-QyQQ6e66f+Ut/qUVjEce0E/wux5nAGLXYZDn1jr15JWstHsCH3l6VVrg8NKDptW9NEiBXKOJeGF/ydxeSDF3IQ==", + "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", - "optional": true, - "peer": true, "dependencies": { - "flow-enums-runtime": "^0.0.6" + "callsites": "^3.0.0" }, "engines": { - "node": ">=18.18" + "node": ">=6" } }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "devOptional": true, - "license": "MIT", + "node_modules/parse-asn1": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", + "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", + "dev": true, + "license": "ISC", + "dependencies": { + "asn1.js": "^4.10.1", + "browserify-aes": "^1.2.0", + "evp_bytestokey": "^1.0.3", + "hash-base": "~3.0", + "pbkdf2": "^3.1.2", + "safe-buffer": "^5.2.1" + }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "node_modules/parse-asn1/node_modules/asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 6" + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, - "node_modules/object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha512-ncrLw+X55z7bkl5PnUvHwFK9FcGuFYo9gtjws2XtSzL+aZ8tm830P60WJ0dSmFVaSalWieW5MD7kEdnXda9yJw==", + "node_modules/parse-asn1/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "dev": true, "license": "MIT" }, - "node_modules/octal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/octal/-/octal-1.0.0.tgz", - "integrity": "sha512-nnda7W8d+A3vEIY+UrDQzzboPf1vhs4JYVhff5CDkq9QNoZY7Xrxeo/htox37j9dZf7yNHevZzqtejWgy1vCqQ==", + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "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/parse-png": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", + "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "ee-first": "1.1.1" + "pngjs": "^3.3.0" }, "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "optional": true, "peer": true, @@ -23829,3357 +26134,3882 @@ "node": ">= 0.8" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "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==", "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "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==", "license": "MIT", - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "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": ">=8" } }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "license": "MIT", + "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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.18" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/ora/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/ora/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/path-to-regexp": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "engines": { + "node": ">=8" } }, - "node_modules/ora/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/pbkdf2": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz", + "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", "license": "MIT", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "create-hash": "~1.1.3", + "create-hmac": "^1.1.7", + "ripemd160": "=2.0.1", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.11", + "to-buffer": "^1.2.0" }, "engines": { - "node": ">= 6" + "node": ">=0.12" } }, - "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "node_modules/pbkdf2/node_modules/create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.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==", + "node_modules/pbkdf2/node_modules/hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "inherits": "^2.0.1" } }, - "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==", - "devOptional": true, + "node_modules/pbkdf2/node_modules/ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" - }, + "hash-base": "^2.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12", + "npm": ">=6" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/jet2jet" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "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==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", + "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, + "optional": true, + "peer": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jonschlinkert" } }, - "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==", + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, "engines": { - "node": ">=6" + "node": ">=0.10" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "license": "(MIT AND Zlib)", - "optional": true - }, - "node_modules/papaparse": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.5.3.tgz", - "integrity": "sha512-5QvjGxYVjxO59MGU2lHVYpRWBBtKHnlIAcSe1uNFCkkptUh63NFRj0FJQm7nR67puEruUci/ZkjmEFrjCAyP4A==", - "license": "MIT" - }, - "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==", + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/parse-asn1": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", - "integrity": "sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==", - "dev": true, - "license": "ISC", + "node_modules/pina": { + "version": "0.20.2204228", + "resolved": "https://registry.npmjs.org/pina/-/pina-0.20.2204228.tgz", + "integrity": "sha512-osNVZg36DsDAEPHskmnTINhM5APSLsu+y8guYYX65nidbRPmnv/trAoPnacpfh8aCpsowGWR/DAYtQHZyZq8wQ==", + "license": "UNLICENSED" + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "peer": true, "dependencies": { - "asn1.js": "^4.10.1", - "browserify-aes": "^1.2.0", - "evp_bytestokey": "^1.0.3", - "hash-base": "~3.0", - "pbkdf2": "^3.1.2", - "safe-buffer": "^5.2.1" + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" }, - "engines": { - "node": ">= 0.10" + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/parse-asn1/node_modules/asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", - "dev": true, + "node_modules/pinia-plugin-persistedstate": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.3.tgz", + "integrity": "sha512-Cm819WBj/s5K5DGw55EwbXDtx+EZzM0YR5AZbq9XE3u0xvXwvX2JnWoFpWIcdzISBHqy9H1UiSIUmXyXqWsQRQ==", "license": "MIT", - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "peerDependencies": { + "pinia": "^2.0.0" } }, - "node_modules/parse-asn1/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "dev": true, + "node_modules/pinia/node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" + "peer": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } } }, - "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==", + "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==", + "devOptional": 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": { - "@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" + "find-up": "^4.0.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/parse-png": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-2.1.0.tgz", - "integrity": "sha512-Nt/a5SfCLiTnQAjx3fHlqp8hRgTL3z7kTQZzvIMS9uCAepnCyjpdEc6M/sz69WqMBdaDBw9sF1F1UaHROYzGkQ==", + "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", - "optional": true, - "peer": true, "dependencies": { - "pngjs": "^3.3.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=10" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/path-browserify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "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" - }, - "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==", "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, "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==", + "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": ">=0.10.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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/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==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", + "node_modules/playwright": { + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz", + "integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "playwright-core": "1.54.2" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", + "node_modules/playwright-core": { + "version": "1.54.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz", + "integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" } }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/pbkdf2": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz", - "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "license": "MIT", "dependencies": { - "create-hash": "~1.1.3", - "create-hmac": "^1.1.7", - "ripemd160": "=2.0.1", - "safe-buffer": "^5.2.1", - "sha.js": "^2.4.11", - "to-buffer": "^1.2.0" + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" }, "engines": { - "node": ">=0.12" + "node": ">=10.4.0" } }, - "node_modules/pbkdf2/node_modules/create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", + "node_modules/plist/node_modules/@xmldom/xmldom": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "sha.js": "^2.4.0" + "engines": { + "node": ">=10.0.0" } }, - "node_modules/pbkdf2/node_modules/hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", + "node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", "license": "MIT", - "dependencies": { - "inherits": "^2.0.1" + "optional": true, + "peer": true, + "engines": { + "node": ">=4.0.0" } }, - "node_modules/pbkdf2/node_modules/ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "license": "MIT", - "dependencies": { - "hash-base": "^2.0.0", - "inherits": "^2.0.1" + "engines": { + "node": ">= 0.4" } }, - "node_modules/pe-library": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", - "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", - "engines": { - "node": ">=12", - "npm": ">=6" + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jet2jet" + "engines": { + "node": "^10 || ^12 || >=14" } }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "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==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, "engines": { - "node": ">=10" + "node": ">=14.0.0" }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "peerDependencies": { + "postcss": "^8.0.0" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", "dev": true, "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, "engines": { - "node": ">=0.10.0" + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" } }, - "node_modules/pina": { - "version": "0.20.2204228", - "resolved": "https://registry.npmjs.org/pina/-/pina-0.20.2204228.tgz", - "integrity": "sha512-osNVZg36DsDAEPHskmnTINhM5APSLsu+y8guYYX65nidbRPmnv/trAoPnacpfh8aCpsowGWR/DAYtQHZyZq8wQ==", - "license": "UNLICENSED" - }, - "node_modules/pinia": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", - "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", - "peer": true, "dependencies": { - "@vue/devtools-api": "^6.6.3", - "vue-demi": "^0.14.10" + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" }, - "funding": { - "url": "https://github.com/sponsors/posva" + "engines": { + "node": ">= 14" }, "peerDependencies": { - "typescript": ">=4.4.4", - "vue": "^2.7.0 || ^3.5.11" + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" }, "peerDependenciesMeta": { - "typescript": { + "postcss": { + "optional": true + }, + "ts-node": { "optional": true } } }, - "node_modules/pinia-plugin-persistedstate": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-3.2.3.tgz", - "integrity": "sha512-Cm819WBj/s5K5DGw55EwbXDtx+EZzM0YR5AZbq9XE3u0xvXwvX2JnWoFpWIcdzISBHqy9H1UiSIUmXyXqWsQRQ==", + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, "peerDependencies": { - "pinia": "^2.0.0" + "postcss": "^8.2.14" } }, - "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==", - "devOptional": true, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, "engines": { - "node": ">= 6" + "node": ">=4" } }, - "node_modules/pkg-dir": { + "node_modules/postcss-value-parser": { "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==", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true, + "license": "MIT" + }, + "node_modules/postject": { + "version": "1.0.0-alpha.6", + "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", + "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "find-up": "^4.0.0" + "commander": "^9.4.0" + }, + "bin": { + "postject": "dist/cli.js" }, "engines": { - "node": ">=8" + "node": ">=14.0.0" } }, - "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, + "node_modules/postject/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "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==", + "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", - "dependencies": { - "p-locate": "^4.1.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "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==", + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": ">=6" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "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==", + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, "license": "MIT", "dependencies": { - "p-limit": "^2.2.0" + "fast-diff": "^1.1.2" }, "engines": { - "node": ">=8" + "node": ">=6.0.0" } }, - "node_modules/playwright": { - "version": "1.54.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz", - "integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.54.2" - }, - "bin": { - "playwright": "cli.js" - }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=18" + "node": ">=6" }, - "optionalDependencies": { - "fsevents": "2.3.2" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/playwright-core": { - "version": "1.54.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz", - "integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==", + "node_modules/pretty-format": { + "version": "30.0.5", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", + "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.5", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" }, "engines": { - "node": ">=18" + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "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, - "hasInstallScript": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/plist": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", - "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", - "license": "MIT", - "dependencies": { - "@xmldom/xmldom": "^0.8.8", - "base64-js": "^1.5.1", - "xmlbuilder": "^15.1.1" - }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "license": "ISC", "engines": { - "node": ">=10.4.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/plist/node_modules/@xmldom/xmldom": { - "version": "0.8.10", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", - "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">= 0.6.0" } }, - "node_modules/pngjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", - "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "license": "MIT", "optional": true, "peer": true, "engines": { - "node": ">=4.0.0" + "node": ">=0.4.0" } }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", "license": "MIT", - "engines": { - "node": ">= 0.4" + "optional": true, + "peer": true, + "dependencies": { + "asap": "~2.0.6" } }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "err-code": "^2.0.2", + "retry": "^0.12.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=10" } }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, + "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==", "license": "MIT", "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" + "node": ">= 6" } }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/protons-runtime": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.6.0.tgz", + "integrity": "sha512-/Kde+sB9DsMFrddJT/UZWe6XqvL7SL5dbag/DBCElFKhkwDj7XKt53S+mzLyaDP5OqS0wXjV5SA572uWDaT0Hg==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8-varint": "^2.0.2", + "uint8arraylist": "^2.4.3", + "uint8arrays": "^5.0.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "license": "MIT" + }, + "node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "license": "MIT", "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "license": "MIT", "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } + "node": ">=6" } }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", "dev": true, "funding": [ { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" }, { - "type": "github", - "url": "https://github.com/sponsors/ai" + "type": "opencollective", + "url": "https://opencollective.com/fast-check" } ], + "license": "MIT" + }, + "node_modules/pvtsutils": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", + "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" + "tslib": "^2.8.1" } }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, + "node_modules/pvtsutils/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", "dev": true, - "license": "MIT" - }, - "node_modules/postject": { - "version": "1.0.0-alpha.6", - "resolved": "https://registry.npmjs.org/postject/-/postject-1.0.0-alpha.6.tgz", - "integrity": "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A==", "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "commander": "^9.4.0" - }, - "bin": { - "postject": "dist/cli.js" - }, "engines": { - "node": ">=14.0.0" + "node": ">=0.6.0", + "teleport": ">=0.2.0" } }, - "node_modules/postject/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "node_modules/qr-code-generator-vue3": { + "version": "1.4.21", + "resolved": "https://registry.npmjs.org/qr-code-generator-vue3/-/qr-code-generator-vue3-1.4.21.tgz", + "integrity": "sha512-UOnhze8vm3ej9A5w7Fl7cO2nOEJ7Nao4qgW60vlmlmaDIg0+CiDMRs3Yy6PK7Ba3Xz77bqQA7l7kJqnM1ti17g==", "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": "^12.20.0 || >=14" + "dependencies": { + "@chenfengyuan/vue-qrcode": "^1.0.2", + "qrcode-generator": "^1.4.3" } }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "node_modules/qr-code-generator-vue3/node_modules/@chenfengyuan/vue-qrcode": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@chenfengyuan/vue-qrcode/-/vue-qrcode-1.0.2.tgz", + "integrity": "sha512-hwy1d4YMJAyEh+V7dLPG8eAKACRvugzSB4ylwb6QNqo84KHTF50/5EJcBYdUhTRPfAqrxG0i6jDAXONWOGyQbQ==", "license": "MIT", "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" + "qrcode": "^1.4.4" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "vue": "^2.6.0" } }, - "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/qr-code-generator-vue3/node_modules/@vue/compiler-sfc": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", + "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "peer": true, + "dependencies": { + "@babel/parser": "^7.23.5", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" } }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, + "node_modules/qr-code-generator-vue3/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "license": "MIT", + "optional": true, + "peer": true, "bin": { - "prettier": "bin/prettier.cjs" + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=14" + "node": ">=10.13.0" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, + "node_modules/qr-code-generator-vue3/node_modules/vue": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", + "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", "license": "MIT", + "peer": true, "dependencies": { - "fast-diff": "^1.1.2" + "@vue/compiler-sfc": "2.7.16", + "csstype": "^3.1.0" + } + }, + "node_modules/qrcode": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", + "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "license": "MIT", + "dependencies": { + "dijkstrajs": "^1.0.1", + "pngjs": "^5.0.0", + "yargs": "^15.3.1" + }, + "bin": { + "qrcode": "bin/qrcode" }, "engines": { - "node": ">=6.0.0" + "node": ">=10.13.0" } }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "license": "MIT", + "node_modules/qrcode-generator": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.2.tgz", + "integrity": "sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw==", + "license": "MIT" + }, + "node_modules/qrcode-terminal": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", + "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", "optional": true, "peer": true, - "engines": { - "node": ">=6" + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/qrcode/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/qrcode/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==", + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=8" } }, - "node_modules/pretty-format": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.5.tgz", - "integrity": "sha512-D1tKtYvByrBkFLe2wHJl2bwMJIiT8rW+XA+TiataH79/FszLQMrpGEvzUVkzPau7OCO0Qnrhpe87PqtOAIB8Yw==", - "dev": true, + "node_modules/qrcode/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==", "license": "MIT", "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" + "p-locate": "^4.1.0" }, "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=8" } }, - "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, + "node_modules/qrcode/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==", "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/proc-log": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", - "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", - "license": "ISC", - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "node_modules/qrcode/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==", "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, "engines": { - "node": ">= 0.6.0" + "node": ">=8" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "node_modules/qrcode/node_modules/pngjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", + "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": ">=0.4.0" + "node": ">=10.13.0" } }, - "node_modules/promise": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", - "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "node_modules/qrcode/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "asap": "~2.0.6" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "node_modules/qrcode/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "license": "ISC" }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "node_modules/qrcode/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "license": "MIT", "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "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==", - "license": "MIT", + "node_modules/qrcode/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "license": "ISC", "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prompts/node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", "engines": { "node": ">=6" } }, - "node_modules/protons-runtime": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/protons-runtime/-/protons-runtime-5.6.0.tgz", - "integrity": "sha512-/Kde+sB9DsMFrddJT/UZWe6XqvL7SL5dbag/DBCElFKhkwDj7XKt53S+mzLyaDP5OqS0wXjV5SA572uWDaT0Hg==", - "license": "Apache-2.0 OR MIT", + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "uint8-varint": "^2.0.2", - "uint8arraylist": "^2.4.3", - "uint8arrays": "^5.0.1" + "inherits": "~2.0.3" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, + "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==", + "devOptional": 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/public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "license": "MIT", - "dependencies": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "dev": true, - "license": "MIT" + "node_modules/ramda": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "license": "MIT", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "safe-buffer": "^5.1.0" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=6" + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "dev": true, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/pvtsutils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", - "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", - "license": "MIT", + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { - "tslib": "^2.8.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" } }, - "node_modules/pvtsutils/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, - "node_modules/pvutils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", - "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", "engines": { - "node": ">=6.0.0" + "node": ">=0.10.0" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", - "dev": true, - "license": "MIT", + "node_modules/rdf-canonize": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.4.0.tgz", + "integrity": "sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==", + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "setimmediate": "^1.0.5" + }, "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" + "node": ">=12" } }, - "node_modules/qr-code-generator-vue3": { - "version": "1.4.21", - "resolved": "https://registry.npmjs.org/qr-code-generator-vue3/-/qr-code-generator-vue3-1.4.21.tgz", - "integrity": "sha512-UOnhze8vm3ej9A5w7Fl7cO2nOEJ7Nao4qgW60vlmlmaDIg0+CiDMRs3Yy6PK7Ba3Xz77bqQA7l7kJqnM1ti17g==", + "node_modules/react": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "license": "MIT", - "dependencies": { - "@chenfengyuan/vue-qrcode": "^1.0.2", - "qrcode-generator": "^1.4.3" + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/qr-code-generator-vue3/node_modules/@chenfengyuan/vue-qrcode": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@chenfengyuan/vue-qrcode/-/vue-qrcode-1.0.2.tgz", - "integrity": "sha512-hwy1d4YMJAyEh+V7dLPG8eAKACRvugzSB4ylwb6QNqo84KHTF50/5EJcBYdUhTRPfAqrxG0i6jDAXONWOGyQbQ==", + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "qrcode": "^1.4.4" - }, - "peerDependencies": { - "vue": "^2.6.0" + "shell-quote": "^1.6.1", + "ws": "^7" } }, - "node_modules/qr-code-generator-vue3/node_modules/@vue/compiler-sfc": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", - "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "node_modules/react-devtools-core/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "optional": true, "peer": true, - "dependencies": { - "@babel/parser": "^7.23.5", - "postcss": "^8.4.14", - "source-map": "^0.6.1" + "engines": { + "node": ">=8.3.0" }, - "optionalDependencies": { - "prettier": "^1.18.2 || ^2.0.0" + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/qr-code-generator-vue3/node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/react-native": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.81.0.tgz", + "integrity": "sha512-RDWhewHGsAa5uZpwIxnJNiv5tW2y6/DrQUjEBdAHPzGMwuMTshern2s4gZaWYeRU3SQguExVddCjiss9IBhxqA==", "license": "MIT", "optional": true, "peer": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@react-native/assets-registry": "0.81.0", + "@react-native/codegen": "0.81.0", + "@react-native/community-cli-plugin": "0.81.0", + "@react-native/gradle-plugin": "0.81.0", + "@react-native/js-polyfills": "0.81.0", + "@react-native/normalize-colors": "0.81.0", + "@react-native/virtualized-lists": "0.81.0", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-jest": "^29.7.0", + "babel-plugin-syntax-hermes-parser": "0.29.1", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "glob": "^7.1.1", + "invariant": "^2.2.4", + "jest-environment-node": "^29.7.0", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.83.1", + "metro-source-map": "^0.83.1", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.26.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "whatwg-fetch": "^3.0.0", + "ws": "^6.2.3", + "yargs": "^17.6.2" + }, "bin": { - "prettier": "bin-prettier.js" + "react-native": "cli.js" }, "engines": { - "node": ">=10.13.0" + "node": ">= 20.19.4" }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" + "peerDependencies": { + "@types/react": "^19.1.0", + "react": "^19.1.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/qr-code-generator-vue3/node_modules/vue": { - "version": "2.7.16", - "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", - "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", - "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "node_modules/react-native-edge-to-edge": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz", + "integrity": "sha512-2WCNdE3Qd6Fwg9+4BpbATUxCLcouF6YRY7K+J36KJ4l3y+tWN6XCqAC4DuoGblAAbb2sLkhEDp4FOlbOIot2Og==", "license": "MIT", + "optional": true, "peer": true, - "dependencies": { - "@vue/compiler-sfc": "2.7.16", - "csstype": "^3.1.0" + "peerDependencies": { + "react": "*", + "react-native": "*" } }, - "node_modules/qrcode": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", - "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", + "node_modules/react-native-securerandom": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz", + "integrity": "sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw==", "license": "MIT", + "optional": true, "dependencies": { - "dijkstrajs": "^1.0.1", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" + "base64-js": "*" }, - "bin": { - "qrcode": "bin/qrcode" + "peerDependencies": { + "react-native": "*" + } + }, + "node_modules/react-native/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==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">=10.13.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/qrcode-generator": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.5.2.tgz", - "integrity": "sha512-pItrW0Z9HnDBnFmgiNrY1uxRdri32Uh9EjNYLPVC2zZ3ZRIIEqBoDgm4DkvDwNNDHTK7FNkmr8zAa77BYc9xNw==", - "license": "MIT" - }, - "node_modules/qrcode-terminal": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.11.0.tgz", - "integrity": "sha512-Uu7ii+FQy4Qf82G4xu7ShHhjhGahEpCWc3x8UavY3CTcWV+ufmmCtwkr7ZKsX42jdL0kr1B5FKUeqJvAn51jzQ==", + "node_modules/react-native/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==", + "license": "MIT", "optional": true, "peer": true, - "bin": { - "qrcode-terminal": "bin/qrcode-terminal.js" + "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/qrcode/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "license": "ISC", + "node_modules/react-native/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==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/qrcode/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==", + "node_modules/react-native/node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@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": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/qrcode/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==", + "node_modules/react-native/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==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "p-locate": "^4.1.0" + "@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": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/qrcode/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==", + "node_modules/react-native/node_modules/@react-native/codegen": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.81.0.tgz", + "integrity": "sha512-gPFutgtj8YqbwKKt3YpZKamUBGd9YZJV51Jq2aiDZ9oThkg1frUBa20E+Jdi7jKn982wjBMxAklAR85QGQ4xMA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "p-try": "^2.0.0" + "glob": "^7.1.1", + "hermes-parser": "0.29.1", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "yargs": "^17.6.2" }, "engines": { - "node": ">=6" + "node": ">= 20.19.4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "*" } }, - "node_modules/qrcode/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==", + "node_modules/react-native/node_modules/@react-native/normalize-colors": { + "version": "0.81.0", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.81.0.tgz", + "integrity": "sha512-3gEu/29uFgz+81hpUgdlOojM4rjHTIPwxpfygFNY60V6ywZih3eLDTS8kAjNZfPFHQbcYrNorJzwnL5yFF/uLw==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/react-native/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==", "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/react-native/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==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/qrcode/node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", + "node_modules/react-native/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==", "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=10.13.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/qrcode/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/react-native/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==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "@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": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "node_modules/qrcode/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC" - }, - "node_modules/qrcode/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "license": "MIT", + "node_modules/react-native/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==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "@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/qrcode/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "license": "ISC", + "node_modules/react-native/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==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" }, "engines": { - "node": ">=6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.29.1.tgz", + "integrity": "sha512-2WFYnoWGdmih1I1J5eIqxATOeycOqRwYxAQBu3cUu/rhwInwHUg7k60AFNbuGjSDL8tje5GDrAnxzRLcu2pYcA==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "inherits": "~2.0.3" + "hermes-parser": "0.29.1" } }, - "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==", - "devOptional": 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/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "node_modules/react-native/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==", "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/react-native/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=18" } }, - "node_modules/ramda": { + "node_modules/react-native/node_modules/hermes-estree": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.29.1.tgz", + "integrity": "sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/react-native/node_modules/hermes-parser": { "version": "0.29.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", - "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.29.1.tgz", + "integrity": "sha512-xBHWmUtRC5e/UL0tI7Ivt2riA/YBq9+SiYFU7C1oBa/j2jYGlIF9043oak1F47ihuDIxQ5nbsKueYJDRY02UgA==", "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ramda" + "optional": true, + "peer": true, + "dependencies": { + "hermes-estree": "0.29.1" } }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "license": "MIT", + "node_modules/react-native/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==", + "license": "BSD-3-Clause", + "optional": true, + "peer": true, "dependencies": { - "safe-buffer": "^5.1.0" + "@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/randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "node_modules/react-native/node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/react-native/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==", "license": "MIT", "optional": true, "peer": true, + "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": ">= 0.6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "node_modules/react-native/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==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "@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" }, - "bin": { - "rc": "cli.js" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/rc/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/rc/node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "node_modules/react-native/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==", "license": "MIT", + "optional": true, + "peer": true, + "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": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/rdf-canonize": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/rdf-canonize/-/rdf-canonize-3.4.0.tgz", - "integrity": "sha512-fUeWjrkOO0t1rg7B2fdyDTvngj+9RlUyL92vOdiB7c0FPguWVsniIMjEtHH+meLBO9rzkUlUzBVXgWrjI8P9LA==", - "license": "BSD-3-Clause", + "node_modules/react-native/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==", + "license": "MIT", "optional": true, + "peer": true, "dependencies": { - "setimmediate": "^1.0.5" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/react": { - "version": "19.1.1", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", - "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", + "node_modules/react-native/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==", "license": "MIT", "optional": true, "peer": true, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/react-devtools-core": { - "version": "6.1.5", - "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", - "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "node_modules/react-native/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==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "shell-quote": "^1.6.1", - "ws": "^7" + "@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/react-devtools-core/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "node_modules/react-native/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==", "license": "MIT", "optional": true, "peer": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "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==", - "devOptional": true, - "license": "MIT" + "node_modules/react-native/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "node_modules/react-native": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.80.2.tgz", - "integrity": "sha512-6ySV4qTJo/To3lgpG/9Mcg/ZtvExqOVZuT7JVGcO5rS2Bjvl/yUAkQF0hTnbRb2Ch6T5MlKghrM4OeHX+KA9Pg==", + "node_modules/react-native/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==", "license": "MIT", "optional": true, "peer": true, - "dependencies": { - "@jest/create-cache-key-function": "^29.7.0", - "@react-native/assets-registry": "0.80.2", - "@react-native/codegen": "0.80.2", - "@react-native/community-cli-plugin": "0.80.2", - "@react-native/gradle-plugin": "0.80.2", - "@react-native/js-polyfills": "0.80.2", - "@react-native/normalize-colors": "0.80.2", - "@react-native/virtualized-lists": "0.80.2", - "abort-controller": "^3.0.0", - "anser": "^1.4.9", - "ansi-regex": "^5.0.0", - "babel-jest": "^29.7.0", - "babel-plugin-syntax-hermes-parser": "0.28.1", - "base64-js": "^1.5.1", - "chalk": "^4.0.0", - "commander": "^12.0.0", - "flow-enums-runtime": "^0.0.6", - "glob": "^7.1.1", - "invariant": "^2.2.4", - "jest-environment-node": "^29.7.0", - "memoize-one": "^5.0.0", - "metro-runtime": "^0.82.2", - "metro-source-map": "^0.82.2", - "nullthrows": "^1.1.1", - "pretty-format": "^29.7.0", - "promise": "^8.3.0", - "react-devtools-core": "^6.1.1", - "react-refresh": "^0.14.0", - "regenerator-runtime": "^0.13.2", - "scheduler": "0.26.0", - "semver": "^7.1.3", - "stacktrace-parser": "^0.1.10", - "whatwg-fetch": "^3.0.0", - "ws": "^6.2.3", - "yargs": "^17.6.2" + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, - "bin": { - "react-native": "cli.js" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/react-native/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==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=10" }, - "peerDependencies": { - "@types/react": "^19.1.0", - "react": "^19.1.0" + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/react-native/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==", + "license": "ISC", + "optional": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/react-native-edge-to-edge": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/react-native-edge-to-edge/-/react-native-edge-to-edge-1.6.0.tgz", - "integrity": "sha512-2WCNdE3Qd6Fwg9+4BpbATUxCLcouF6YRY7K+J36KJ4l3y+tWN6XCqAC4DuoGblAAbb2sLkhEDp4FOlbOIot2Og==", + "node_modules/react-native/node_modules/ws": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", + "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", "license": "MIT", "optional": true, "peer": true, - "peerDependencies": { - "react": "*", - "react-native": "*" + "dependencies": { + "async-limiter": "~1.0.0" } }, - "node_modules/react-native-securerandom": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/react-native-securerandom/-/react-native-securerandom-0.1.1.tgz", - "integrity": "sha512-CozcCx0lpBLevxiXEb86kwLRalBCHNjiGPlw3P7Fi27U6ZLdfjOCNRHD1LtBKcvPvI3TvkBXB3GOtLvqaYJLGw==", + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", "license": "MIT", "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "license": "MIT", "dependencies": { - "base64-js": "*" + "debug": "^4.3.4" }, - "peerDependencies": { - "react-native": "*" + "bin": { + "read-binary-file-arch": "cli.js" } }, - "node_modules/react-native/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==", + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" + "pify": "^2.3.0" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/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==", + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "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" + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/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==", + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@sinclair/typebox": "^0.27.8" + "locate-path": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "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" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/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==", + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "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" + "p-try": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/node_modules/@react-native/codegen": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.80.2.tgz", - "integrity": "sha512-eYad9ex9/RS6oFbbpu6LxsczktbhfJbJlTvtRlcWLJjJbFTeNr5Q7CgBT2/m5VtpxnJ/0YdmZ9vdazsJ2yp9kw==", + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "glob": "^7.1.1", - "hermes-parser": "0.28.1", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "yargs": "^17.6.2" + "p-limit": "^1.1.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@babel/core": "*" + "node": ">=4" } }, - "node_modules/react-native/node_modules/@react-native/normalize-colors": { - "version": "0.80.2", - "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.80.2.tgz", - "integrity": "sha512-08Ax7554Z31NXi5SQ6h1GsiSrlZEOYHQNSC7u+x91Tdiq87IXldW8Ib1N3ThXoDcD8bjr+I+MdlabEJw36/fFg==", + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true + "engines": { + "node": ">=4" + } }, - "node_modules/react-native/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==", + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true + "engines": { + "node": ">=4" + } }, - "node_modules/react-native/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==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, - "node_modules/react-native/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==", + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=10" + "dependencies": { + "pify": "^3.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=4" } }, - "node_modules/react-native/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==", + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "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": ">=4" } }, - "node_modules/react-native/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==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "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/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" } }, - "node_modules/react-native/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==", + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/react-native/node_modules/babel-plugin-syntax-hermes-parser": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.28.1.tgz", - "integrity": "sha512-meT17DOuUElMNsL5LZN56d+KBp22hb0EfxWfuPUeoSi54e40v1W4C2V36P75FpsH9fVEfDKpw5Nnkahc8haSsQ==", + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "hermes-parser": "0.28.1" + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "node_modules/react-native/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==", + "node_modules/readdirp/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", - "optional": true, - "peer": true, - "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" + "node": ">=8.6" }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/react-native/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/react-native/node_modules/hermes-estree": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.28.1.tgz", - "integrity": "sha512-w3nxl/RGM7LBae0v8LH2o36+8VqwOZGv9rX1wyoWT6YaKZLqpJZ0YQ5P0LVr3tuRpf7vCx0iIG4i/VmBJejxTQ==", + "node_modules/reflect-metadata": { + "version": "0.1.14", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", + "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", + "license": "Apache-2.0" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "license": "MIT", "optional": true, "peer": true }, - "node_modules/react-native/node_modules/hermes-parser": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.28.1.tgz", - "integrity": "sha512-nf8o+hE8g7UJWParnccljHumE9Vlq8F7MqIdeahl+4x0tvCUJYRrT0L7h0MMg/X9YJmkNwsfbaNNrzPtFXOscg==", + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "hermes-estree": "0.28.1" - } - }, - "node_modules/react-native/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==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true, - "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" + "regenerate": "^1.4.2" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/react-native/node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", "optional": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - } + "peer": true }, - "node_modules/react-native/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==", + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "dev": true, + "license": "MIT" + }, + "node_modules/regexpu-core": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "license": "MIT", "optional": true, "peer": true, "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" + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.12.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/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==", - "license": "MIT", - "optional": true, - "peer": true, - "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/register-service-worker": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.2.tgz", + "integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==", + "license": "MIT" + }, + "node_modules/registry-auth-token": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" } }, - "node_modules/react-native/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==", + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "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" + "rc": "^1.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/react-native/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==", + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", "license": "MIT", "optional": true, + "peer": true + }, + "node_modules/regjsparser": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", + "license": "BSD-2-Clause", + "optional": true, "peer": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "jsesc": "~3.0.2" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "bin": { + "regjsparser": "bin/parser" } }, - "node_modules/react-native/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==", + "node_modules/regjsparser/node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", "license": "MIT", "optional": true, "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6" } }, - "node_modules/react-native/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==", + "node_modules/replace": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/replace/-/replace-1.2.2.tgz", + "integrity": "sha512-C4EDifm22XZM2b2JOYe6Mhn+lBsLBAvLbK8drfUQLTfD1KYl/n3VaW/CDju0Ny4w3xTtegBpg8YNSpFJPUDSjA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "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" + "chalk": "2.4.2", + "minimatch": "3.0.5", + "yargs": "^15.3.1" + }, + "bin": { + "replace": "bin/replace.js", + "search": "bin/search.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/react-native/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==", + "node_modules/replace/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "color-convert": "^1.9.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/replace/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", - "optional": true, - "peer": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/react-native/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==", + "node_modules/replace/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/react-native/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==", + "node_modules/replace/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/replace/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "has-flag": "^4.0.0" - }, + "color-name": "1.1.3" + } + }, + "node_modules/replace/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/replace/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=0.8.0" } }, - "node_modules/react-native/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==", - "license": "ISC", - "optional": true, - "peer": true, + "node_modules/replace/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": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/react-native/node_modules/ws": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz", - "integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==", + "node_modules/replace/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "async-limiter": "~1.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "node_modules/replace/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", - "optional": true, - "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/read-binary-file-arch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", - "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "node_modules/replace/node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/replace/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": { - "debug": "^4.3.4" + "p-try": "^2.0.0" }, - "bin": { - "read-binary-file-arch": "cli.js" + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "node_modules/replace/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": { - "pify": "^2.3.0" + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "node_modules/replace/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" + "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, - "node_modules/read-pkg-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", - "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "node_modules/replace/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, "license": "MIT", "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^3.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "node_modules/replace/node_modules/wrap-ansi/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": { - "locate-path": "^2.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "node_modules/replace/node_modules/wrap-ansi/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": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=4" + "node": ">=7.0.0" } }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "node_modules/replace/node_modules/wrap-ansi/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/replace/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/replace/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, "license": "MIT", "dependencies": { - "p-try": "^1.0.0" + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "node_modules/replace/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "p-limit": "^1.1.0" + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" }, "engines": { - "node": ">=4" + "node": ">=6" } }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", - "dev": true, + "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==", "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", - "dev": true, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "devOptional": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/read-pkg/node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "license": "ISC" }, - "node_modules/read-pkg/node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/requireg": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", + "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", + "optional": true, + "peer": true, "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "nested-error-stacks": "~2.0.1", + "rc": "~1.2.7", + "resolve": "~1.7.1" + }, + "engines": { + "node": ">= 4.0.0" } }, - "node_modules/read-pkg/node_modules/path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, + "node_modules/requireg/node_modules/resolve": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", + "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "pify": "^3.0.0" - }, - "engines": { - "node": ">=4" + "path-parse": "^1.0.5" } }, - "node_modules/read-pkg/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, "engines": { - "node": ">=4" + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" } }, - "node_modules/read-pkg/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", + "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==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, "bin": { - "semver": "bin/semver" + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "license": "MIT" + }, + "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": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" + "resolve-from": "^5.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=8" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "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", - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">=8" } }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "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": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "node": ">=4" } }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "node_modules/resolve-global": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", + "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", "dev": true, "license": "MIT", "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" + "global-dirs": "^0.1.1" }, "engines": { "node": ">=8" } }, - "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "license": "Apache-2.0" + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "node_modules/resolve-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz", + "integrity": "sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw==", "license": "MIT", "optional": true, "peer": true }, - "node_modules/regenerate-unicode-properties": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "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==", "license": "MIT", "optional": true, "peer": true, - "dependencies": { - "regenerate": "^1.4.2" - }, "engines": { - "node": ">=4" + "node": ">=10" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/regexp-to-ast": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", - "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", - "dev": true, - "license": "MIT" + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/regexpu-core": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/register-service-worker": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/register-service-worker/-/register-service-worker-1.7.2.tgz", - "integrity": "sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==", - "license": "MIT" + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "engines": { + "node": ">= 4" + } }, - "node_modules/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "devOptional": true, "license": "MIT", - "optional": true, - "peer": true + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "node_modules/regjsparser": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "license": "BSD-2-Clause", - "optional": true, - "peer": true, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", + "dev": true, + "license": "ISC", "dependencies": { - "jsesc": "~3.0.2" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { - "regjsparser": "bin/parser" + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/rimraf/node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, "bin": { - "jsesc": "bin/jsesc" + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=6" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/replace": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/replace/-/replace-1.2.2.tgz", - "integrity": "sha512-C4EDifm22XZM2b2JOYe6Mhn+lBsLBAvLbK8drfUQLTfD1KYl/n3VaW/CDju0Ny4w3xTtegBpg8YNSpFJPUDSjA==", + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "dependencies": { - "chalk": "2.4.2", - "minimatch": "3.0.5", - "yargs": "^15.3.1" - }, - "bin": { - "replace": "bin/replace.js", - "search": "bin/search.js" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">= 6" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/replace/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, - "node_modules/replace/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", + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "license": "MPL-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" } }, - "node_modules/replace/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/rollup": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.4.tgz", + "integrity": "sha512-YbxoxvoqNg9zAmw4+vzh1FkGAiZRK+LhnSrbSrSXMdZYsRPDWoshcSd/pldKRO6lWzv/e9TiJAVQyirYIeSIPQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=4" + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.4", + "@rollup/rollup-android-arm64": "4.46.4", + "@rollup/rollup-darwin-arm64": "4.46.4", + "@rollup/rollup-darwin-x64": "4.46.4", + "@rollup/rollup-freebsd-arm64": "4.46.4", + "@rollup/rollup-freebsd-x64": "4.46.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.4", + "@rollup/rollup-linux-arm-musleabihf": "4.46.4", + "@rollup/rollup-linux-arm64-gnu": "4.46.4", + "@rollup/rollup-linux-arm64-musl": "4.46.4", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.4", + "@rollup/rollup-linux-ppc64-gnu": "4.46.4", + "@rollup/rollup-linux-riscv64-gnu": "4.46.4", + "@rollup/rollup-linux-riscv64-musl": "4.46.4", + "@rollup/rollup-linux-s390x-gnu": "4.46.4", + "@rollup/rollup-linux-x64-gnu": "4.46.4", + "@rollup/rollup-linux-x64-musl": "4.46.4", + "@rollup/rollup-win32-arm64-msvc": "4.46.4", + "@rollup/rollup-win32-ia32-msvc": "4.46.4", + "@rollup/rollup-win32-x64-msvc": "4.46.4", + "fsevents": "~2.3.2" } }, - "node_modules/replace/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "node_modules/rollup/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.4.tgz", + "integrity": "sha512-/9qwE/BM7ATw/W/OFEMTm3dmywbJyLQb4f4v5nmOjgYxPIGpw7HaxRi6LnD4Pjn/q7k55FGeHe1/OD02w63apA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/replace/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/rollup/node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.4.tgz", + "integrity": "sha512-QkWfNbeRuzFnv2d0aPlrzcA3Ebq2mE8kX/5Pl7VdRShbPBjSnom7dbT8E3Jmhxo2RL784hyqGvR5KHavCJQciw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/replace/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.4.tgz", + "integrity": "sha512-SWuXdnsayCZL4lXoo6jn0yyAj7TTjWE4NwDVt9s7cmu6poMhtiras5c8h6Ih6Y0Zk6Z+8t/mLumvpdSPTWub2Q==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/replace/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.4.tgz", + "integrity": "sha512-vDknMDqtMhrrroa5kyX6tuC0aRZZlQ+ipDfbXd2YGz5HeV2t8HOl/FDAd2ynhs7Ki5VooWiiZcCtxiZ4IjqZwQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=0.8.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/replace/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==", + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.4.tgz", + "integrity": "sha512-0Xj1vZE3cbr/wda8d/m+UeuSL+TDpuozzdD4QaSzu/xSOMK0Su5RhIkF7KVHFQsobemUNHPLEcYllL7ZTCP/Cg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/replace/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.4.tgz", + "integrity": "sha512-kM/orjpolfA5yxsx84kI6bnK47AAZuWxglGKcNmokw2yy9i5eHY5UAjcX45jemTJnfHAWo3/hOoRqEeeTdL5hw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=4" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/replace/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==", + "node_modules/rollup/node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.4.tgz", + "integrity": "sha512-cNLH4psMEsWKILW0isbpQA2OvjXLbKvnkcJFmqAptPQbtLrobiapBJVj6RoIvg6UXVp5w0wnIfd/Q56cNpF+Ew==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/replace/node_modules/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "node_modules/rollup/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.4.tgz", + "integrity": "sha512-IKL9mewGZ5UuuX4NQlwOmxPyqielvkAPUS2s1cl6yWjjQvyN3h5JTdVFGD5Jr5xMjRC8setOfGQDVgX8V+dkjg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/run-con": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", + "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { - "brace-expansion": "^1.1.7" + "deep-extend": "^0.6.0", + "ini": "~4.1.0", + "minimist": "^1.2.8", + "strip-json-comments": "~3.1.1" }, - "engines": { - "node": "*" + "bin": { + "run-con": "cli.js" } }, - "node_modules/replace/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==", + "node_modules/run-con/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, + "license": "ISC", "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/replace/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, + "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==", + "devOptional": 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": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" + "queue-microtask": "^1.2.2" } }, - "node_modules/replace/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" + "tslib": "^2.1.0" } }, - "node_modules/replace/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "license": "MIT", + "node_modules/safari-14-idb-fix": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/safari-14-idb-fix/-/safari-14-idb-fix-1.0.6.tgz", + "integrity": "sha512-oTEQOdMwRX+uCtWCKT1nx2gAeSdpr8elg/2gcaKUH00SJU2xWESfkx11nmXwTRHy7xfQoj1o4TTQvdmuBosTnA==", + "license": "Apache-2.0" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "license": "WTFPL OR ISC", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" + "truncate-utf8-bytes": "^1.0.0" } }, - "node_modules/replace/node_modules/wrap-ansi/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, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "license": "ISC" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } + "optional": true, + "peer": true }, - "node_modules/replace/node_modules/wrap-ansi/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, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "license": "MIT" + }, + "node_modules/sdp": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.1.tgz", + "integrity": "sha512-lwsAIzOPlH8/7IIjjz3K0zYBk7aBVVcvjMwt3M4fLxpjMYyy7i3I97SLHebgn4YBjirkzfp3RvRDWSKsh/+WFw==", + "license": "MIT" + }, + "node_modules/secp256k1": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", + "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", + "hasInstallScript": true, "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "elliptic": "^6.5.7", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=18.0.0" } }, - "node_modules/replace/node_modules/wrap-ansi/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, + "node_modules/secp256k1/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", "license": "MIT" }, - "node_modules/replace/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" + "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==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/replace/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, + "node_modules/send": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz", + "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/replace/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "license": "ISC", + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" + "ms": "2.0.0" } }, - "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==", + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/send/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", "optional": true, "peer": true, + "bin": { + "mime": "cli.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "license": "ISC" + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true }, - "node_modules/requireg": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/requireg/-/requireg-0.2.2.tgz", - "integrity": "sha512-nYzyjnFcPNGR3lx9lwPPPnuQxv6JWEZd2Ci0u9opN7N5zUEPIhY/GbL3vMGOr2UXwEg9WwSyV9X9Y/kLFgPsOg==", + "node_modules/send/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "optional": true, "peer": true, "dependencies": { - "nested-error-stacks": "~2.0.1", - "rc": "~1.2.7", - "resolve": "~1.7.1" + "ee-first": "1.1.1" }, "engines": { - "node": ">= 4.0.0" + "node": ">= 0.8" } }, - "node_modules/requireg/node_modules/resolve": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.7.1.tgz", - "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", + "node_modules/send/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", "optional": true, "peer": true, - "dependencies": { - "path-parse": "^1.0.5" + "engines": { + "node": ">= 0.8" } }, - "node_modules/resedit": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", - "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "node_modules/serialize-error": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", "license": "MIT", + "optional": true, "dependencies": { - "pe-library": "^0.4.1" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=12", - "npm": ">=6" + "node": ">=10" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/jet2jet" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", - "devOptional": true, + "node_modules/serve": { + "version": "14.2.4", + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz", + "integrity": "sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==", + "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "@zeit/schemas": "2.36.0", + "ajv": "8.12.0", + "arg": "5.0.2", + "boxen": "7.0.0", + "chalk": "5.0.1", + "chalk-template": "0.4.0", + "clipboardy": "3.0.0", + "compression": "1.7.4", + "is-port-reachable": "4.0.0", + "serve-handler": "6.1.6", + "update-check": "1.5.4" }, "bin": { - "resolve": "bin/resolve" + "serve": "build/main.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 14" } }, - "node_modules/resolve-alpn": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "license": "MIT" + "node_modules/serve-handler": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.0.0", + "content-disposition": "0.5.2", + "mime-types": "2.1.18", + "minimatch": "3.1.2", + "path-is-inside": "1.0.2", + "path-to-regexp": "3.3.0", + "range-parser": "1.2.0" + } }, - "node_modules/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==", + "node_modules/serve-handler/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": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "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==", + "node_modules/serve-handler/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.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==", + "node_modules/serve-handler/node_modules/mime-db": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=4" + "node": ">= 0.6" } }, - "node_modules/resolve-workspace-root": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-workspace-root/-/resolve-workspace-root-2.0.0.tgz", - "integrity": "sha512-IsaBUZETJD5WsI11Wt8PKHwaIe45or6pwNc8yflvLJ4DWtImK9kuLoH5kUva/2Mmx/RdIyr4aONNSa2v9LTJsw==", + "node_modules/serve-handler/node_modules/mime-types": { + "version": "2.1.18", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true + "dependencies": { + "mime-db": "~1.33.0" + }, + "engines": { + "node": ">= 0.6" + } }, - "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==", + "node_modules/serve-handler/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/serve-handler/node_modules/range-parser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": ">=10" + "node": ">= 0.6" } }, - "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "lowercase-keys": "^2.0.0" + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "node_modules/serve-static/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" + "ms": "2.0.0" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "node_modules/serve-static/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/serve-static/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">= 4" + "node": ">= 0.8" } }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "devOptional": true, + "node_modules/serve-static/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "mime": "cli.js" + }, "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": ">=4" } }, - "node_modules/rimraf": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", - "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", - "dev": true, - "license": "ISC", + "node_modules/serve-static/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/serve-static/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "glob": "^11.0.0", - "package-json-from-dist": "^1.0.0" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" + "ee-first": "1.1.1" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", - "dev": true, - "license": "ISC", + "node_modules/serve-static/node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "foreground-child": "^3.3.1", - "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8.0" } }, - "node_modules/rimraf/node_modules/jackspeak": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", - "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, + "node_modules/serve-static/node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8" } }, - "node_modules/rimraf/node_modules/lru-cache": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", - "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", - "dev": true, - "license": "ISC", + "node_modules/serve-static/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": "20 || >=22" + "node": ">= 0.8" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "node_modules/serve/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@isaacs/brace-expansion": "^5.0.0" - }, - "engines": { - "node": "20 || >=22" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/rimraf/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/serve/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "dev": true, - "license": "ISC", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.8" } }, - "node_modules/rimraf/node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "node_modules/serve/node_modules/chalk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, + "license": "MIT", "engines": { - "node": "20 || >=22" + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "node_modules/serve/node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, "license": "MIT", "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "node_modules/rlp": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", - "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", - "license": "MPL-2.0", - "dependencies": { - "bn.js": "^5.2.0" + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" }, - "bin": { - "rlp": "bin/rlp" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/rollup": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", - "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "node_modules/serve/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.46.2", - "@rollup/rollup-android-arm64": "4.46.2", - "@rollup/rollup-darwin-arm64": "4.46.2", - "@rollup/rollup-darwin-x64": "4.46.2", - "@rollup/rollup-freebsd-arm64": "4.46.2", - "@rollup/rollup-freebsd-x64": "4.46.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", - "@rollup/rollup-linux-arm-musleabihf": "4.46.2", - "@rollup/rollup-linux-arm64-gnu": "4.46.2", - "@rollup/rollup-linux-arm64-musl": "4.46.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", - "@rollup/rollup-linux-ppc64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-musl": "4.46.2", - "@rollup/rollup-linux-s390x-gnu": "4.46.2", - "@rollup/rollup-linux-x64-gnu": "4.46.2", - "@rollup/rollup-linux-x64-musl": "4.46.2", - "@rollup/rollup-win32-arm64-msvc": "4.46.2", - "@rollup/rollup-win32-ia32-msvc": "4.46.2", - "@rollup/rollup-win32-x64-msvc": "4.46.2", - "fsevents": "~2.3.2" + "ms": "2.0.0" } }, - "node_modules/rollup/node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", - "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", - "cpu": [ - "arm64" - ], + "node_modules/serve/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "license": "MIT" }, - "node_modules/rollup/node_modules/@rollup/rollup-darwin-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", - "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", - "cpu": [ - "x64" - ], + "node_modules/serve/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] + "license": "MIT" }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", - "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", - "cpu": [ - "arm64" - ], + "node_modules/serve/node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": ">= 0.8" + } }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", - "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", - "cpu": [ - "arm64" - ], + "node_modules/serve/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, + "license": "MIT" + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC", "optional": true, - "os": [ - "linux" - ] + "peer": true + }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", - "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "node_modules/sharp/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "devOptional": true, + "license": "MIT" }, - "node_modules/rollup/node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", - "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/sharp/node_modules/tar-fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", + "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", + "devOptional": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } }, - "node_modules/rollup/node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", - "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", - "cpu": [ - "arm64" - ], - "dev": true, + "node_modules/sharp/node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "devOptional": true, "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } }, - "node_modules/rollup/node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", - "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", - "cpu": [ - "x64" - ], - "dev": true, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/run-con": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.3.2.tgz", - "integrity": "sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==", - "dev": true, - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~4.1.0", - "minimist": "^1.2.8", - "strip-json-comments": "~3.1.1" + "shebang-regex": "^3.0.0" }, - "bin": { - "run-con": "cli.js" + "engines": { + "node": ">=8" } }, - "node_modules/run-con/node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "dev": true, - "license": "ISC", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=8" } }, - "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==", + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", "funding": [ { "type": "github", @@ -27194,31 +30024,12 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safari-14-idb-fix": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/safari-14-idb-fix/-/safari-14-idb-fix-1.0.6.tgz", - "integrity": "sha512-oTEQOdMwRX+uCtWCKT1nx2gAeSdpr8elg/2gcaKUH00SJU2xWESfkx11nmXwTRHy7xfQoj1o4TTQvdmuBosTnA==", - "license": "Apache-2.0" + "license": "MIT" }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "funding": [ { "type": "github", @@ -27233,797 +30044,915 @@ "url": "https://feross.org/support" } ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-plist": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", + "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "bplist-creator": "0.1.0", + "bplist-parser": "0.3.1", + "plist": "^3.0.5" + } + }, + "node_modules/simple-plist/node_modules/bplist-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", + "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "devOptional": true, "license": "MIT" }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-vue-camera": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/simple-vue-camera/-/simple-vue-camera-1.1.3.tgz", + "integrity": "sha512-GVAYq1BMI9cHt+h24tu2dfIFFvhjVQ1M8IkK5LmrKcYoBA8FZlLNlhrHC2NnTPbMAXIvJn1Bqx8X6Q31+Y2+jA==", "license": "MIT" }, - "node_modules/sanitize-filename": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", - "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", - "license": "WTFPL OR ISC", + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "license": "MIT", "dependencies": { - "truncate-utf8-bytes": "^1.0.0" + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/sax": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", - "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", - "license": "ISC" - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "node_modules/slugify": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", + "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", "license": "MIT", "optional": true, - "peer": true + "peer": true, + "engines": { + "node": ">=8.0.0" + } }, - "node_modules/scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "license": "MIT" + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } }, - "node_modules/sdp": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.1.tgz", - "integrity": "sha512-lwsAIzOPlH8/7IIjjz3K0zYBk7aBVVcvjMwt3M4fLxpjMYyy7i3I97SLHebgn4YBjirkzfp3RvRDWSKsh/+WFw==", - "license": "MIT" + "node_modules/smol-toml": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz", + "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } }, - "node_modules/secp256k1": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", - "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", - "hasInstallScript": true, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "license": "MIT", "dependencies": { - "elliptic": "^6.5.7", - "node-addon-api": "^5.0.0", - "node-gyp-build": "^4.2.0" + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" }, "engines": { - "node": ">=18.0.0" + "node": ">= 10.0.0", + "npm": ">= 3.0.0" } }, - "node_modules/secp256k1/node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "license": "MIT" - }, - "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==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">= 10" } }, - "node_modules/send": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.1.tgz", - "integrity": "sha512-p4rRk4f23ynFEfcD9LA0xRYngj+IyGiEYyqqOak8kaN0TvNmuxC2dcVeBn62GpCeR2CpWqyHCNScTP91QbAVFg==", + "node_modules/socks-proxy-agent/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==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "debug": "4" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 6.0.0" } }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "ms": "2.0.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "optional": true, - "peer": true + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true }, - "node_modules/send/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">= 0.8" + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "node_modules/send/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "mime": "cli.js" + "dependencies": { + "through": "2" }, "engines": { - "node": ">=4" + "node": "*" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true, - "peer": true + "node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } }, - "node_modules/send/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/split2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "ee-first": "1.1.1" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 6" } }, - "node_modules/send/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "devOptional": true, + "license": "BSD-3-Clause" + }, + "node_modules/sql-highlight": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", + "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", + "funding": [ + "https://github.com/scriptcoded/sql-highlight?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/scriptcoded" + } + ], "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": ">= 0.8" + "node": ">=14" } }, - "node_modules/serialize-error": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", - "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", - "license": "MIT", - "optional": true, + "node_modules/sql.js": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.13.0.tgz", + "integrity": "sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==", + "license": "MIT" + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", "dependencies": { - "type-fest": "^0.20.2" + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" }, - "engines": { - "node": ">=10" + "optionalDependencies": { + "node-gyp": "8.x" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } } }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "optional": true, - "peer": true, + "node_modules/sqlite3/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "license": "ISC", "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "minipass": "^3.1.1" }, "engines": { - "node": ">= 0.8.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/serve-static/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "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==", + "devOptional": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-static/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/serve-static/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "optional": true, - "peer": true, + "escape-string-regexp": "^2.0.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=10" } }, - "node_modules/serve-static/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "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==", + "devOptional": true, "license": "MIT", - "optional": true, - "peer": true, - "bin": { - "mime": "cli.js" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/serve-static/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==", + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", "license": "MIT", "optional": true, "peer": true }, - "node_modules/serve-static/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", "license": "MIT", "optional": true, "peer": true, "dependencies": { - "ee-first": "1.1.1" + "type-fest": "^0.7.1" }, "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/serve-static/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", "optional": true, "peer": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/serve-static/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", "license": "MIT", - "optional": true, - "peer": true, "engines": { - "node": ">= 0.8" + "node": ">= 6" } }, - "node_modules/serve-static/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "license": "MIT", "optional": true, "peer": true, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "node_modules/str2buf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/str2buf/-/str2buf-1.3.0.tgz", + "integrity": "sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==", "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "optional": true, - "peer": true + "optional": true }, - "node_modules/sha.js": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", - "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.0" - }, - "bin": { - "sha.js": "bin.js" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "license": "MIT", + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" } }, - "node_modules/sharp": { - "version": "0.32.6", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", - "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", + "node_modules/stream-browserify/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.2", - "node-addon-api": "^6.1.0", - "prebuild-install": "^7.1.1", - "semver": "^7.5.4", - "simple-get": "^4.0.1", - "tar-fs": "^3.0.4", - "tunnel-agent": "^0.6.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=14.15.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" + "node": ">= 6" } }, - "node_modules/sharp/node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "node_modules/stream-buffers": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", "devOptional": true, - "license": "MIT" + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } }, - "node_modules/sharp/node_modules/tar-fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.0.tgz", - "integrity": "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==", + "node_modules/streamx": { + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", "devOptional": true, "license": "MIT", "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" }, "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, - "node_modules/sharp/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" + "bare-events": "^2.2.0" } }, - "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==", + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" + "safe-buffer": "~5.2.0" } }, - "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==", + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.6.19" } }, - "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "devOptional": true, + "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", - "engines": { - "node": ">= 0.4" + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "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==", - "license": "ISC" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/string-range": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/string-range/-/string-range-1.2.2.tgz", + "integrity": "sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w==", + "dev": true, "license": "MIT" }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "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==", "license": "MIT", "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/simple-plist": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", - "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", - "devOptional": true, + "node_modules/string-width-cjs": { + "name": "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==", "license": "MIT", "dependencies": { - "bplist-creator": "0.1.0", - "bplist-parser": "0.3.1", - "plist": "^3.0.5" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/simple-plist/node_modules/bplist-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", - "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", - "devOptional": true, + "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==", "license": "MIT", "dependencies": { - "big-integer": "1.6.x" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 5.10.0" + "node": ">=8" } }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "devOptional": true, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { - "is-arrayish": "^0.3.1" + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "devOptional": true, - "license": "MIT" + "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/simple-update-notifier": { + "node_modules/strip-final-newline": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "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-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", "license": "MIT", "dependencies": { - "semver": "^7.5.3" + "is-hex-prefixed": "1.0.0" }, "engines": { - "node": ">=10" + "node": ">=6.5.0", + "npm": ">=3" } }, - "node_modules/simple-vue-camera": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/simple-vue-camera/-/simple-vue-camera-1.1.3.tgz", - "integrity": "sha512-GVAYq1BMI9cHt+h24tu2dfIFFvhjVQ1M8IkK5LmrKcYoBA8FZlLNlhrHC2NnTPbMAXIvJn1Bqx8X6Q31+Y2+jA==", - "license": "MIT" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/slash": { + "node_modules/strip-indent": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "devOptional": true, + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "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", - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/slugify": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", - "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==", + "node_modules/structured-headers": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", + "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", "license": "MIT", "optional": true, - "peer": true, + "peer": true + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, "engines": { - "node": ">=8.0.0" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "devOptional": true, "license": "MIT", "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" + "node": ">= 6" } }, - "node_modules/smol-toml": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.3.4.tgz", - "integrity": "sha512-UOPtVuYkzYGee0Bd2Szz8d2G3RfMfJ2t3qVdZUAozZyAk+a0Sxa+QKix0YCwjL/A1RR0ar44nCxaoN9FxdJGwA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/sponsors/cyyynthia" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/socks": { - "version": "2.8.6", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", - "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", - "license": "MIT", + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "devOptional": true, + "license": "ISC", "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "license": "MIT", - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, + "node_modules/sucrase/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "devOptional": true, + "license": "ISC", "engines": { - "node": ">= 10" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/socks-proxy-agent/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==", + "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==", "license": "MIT", "dependencies": { - "debug": "4" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 6.0.0" + "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==", - "license": "BSD-3-Clause", + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", + "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==", + "devOptional": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/synckit": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "dev": true, "license": "MIT", "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "@pkgr/core": "^0.2.9" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" } }, - "node_modules/spawn-command": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", - "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" } }, - "node_modules/spdx-exceptions": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", - "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, - "license": "CC-BY-3.0" + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", "license": "MIT", "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" } }, - "node_modules/spdx-license-ids": { - "version": "3.0.22", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", - "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", - "dev": true, - "license": "CC0-1.0" + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" }, - "node_modules/split": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", - "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", "license": "MIT", "dependencies": { - "through": "2" + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" }, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dev": true, - "license": "ISC", + "node_modules/tar-stream/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", "dependencies": { - "readable-stream": "^3.0.0" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/split2/node_modules/readable-stream": { + "node_modules/tar-stream/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -28034,740 +30963,684 @@ "node": ">= 6" } }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause" - }, - "node_modules/sql-highlight": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz", - "integrity": "sha512-ed7OK4e9ywpE7pgRMkMQmZDPKSVdm0oX5IEtZiKnFucSF0zu6c80GZBe38UqHuVhTWJ9xsKgSMjCG2bml86KvA==", - "funding": [ - "https://github.com/scriptcoded/sql-highlight?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/scriptcoded" - } - ], - "license": "MIT", + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", "engines": { - "node": ">=14" - } - }, - "node_modules/sql.js": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.13.0.tgz", - "integrity": "sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==", - "license": "MIT" - }, - "node_modules/sqlite3": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "tar": "^6.1.11" - }, - "optionalDependencies": { - "node-gyp": "8.x" - }, - "peerDependencies": { - "node-gyp": "8.x" - }, - "peerDependenciesMeta": { - "node-gyp": { - "optional": true - } + "node": ">=8" } }, - "node_modules/sqlite3/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "license": "ISC", - "dependencies": { - "minipass": "^3.1.1" + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=10" } }, - "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==", - "devOptional": true, + "node_modules/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "license": "MIT", + "peer": true, "dependencies": { - "escape-string-regexp": "^2.0.0" + "mkdirp": "^0.5.1", + "rimraf": "~2.6.2" }, "engines": { - "node": ">=10" + "node": ">=6.0.0" } }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { + "node_modules/temp-dir": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", "devOptional": true, "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", "license": "MIT", - "optional": true, - "peer": true + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } }, - "node_modules/stacktrace-parser": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/tempy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", + "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "dev": true, "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "type-fest": "^0.7.1" + "del": "^6.0.0", + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "node_modules/tempy/node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, "license": "(MIT OR CC0-1.0)", - "optional": true, - "peer": true, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stat-mode": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", - "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "license": "MIT", + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "license": "BSD-2-Clause", "optional": true, "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, "engines": { - "node": ">= 0.6" + "node": ">=10" } }, - "node_modules/str2buf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/str2buf/-/str2buf-1.3.0.tgz", - "integrity": "sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==", - "license": "MIT", - "optional": true - }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "license": "MIT", - "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" - } + "optional": true, + "peer": true }, - "node_modules/stream-browserify/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "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==", + "devOptional": true, + "license": "ISC", "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/stream-buffers": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", - "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "node_modules/test-exclude/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==", "devOptional": true, - "license": "Unlicense", - "engines": { - "node": ">= 0.10.0" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/streamx": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", - "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "devOptional": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "fast-fifo": "^1.3.2", - "text-decoder": "^1.1.0" + "brace-expansion": "^1.1.7" }, - "optionalDependencies": { - "bare-events": "^2.2.0" + "engines": { + "node": "*" } }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "devOptional": true, + "license": "Apache-2.0", "dependencies": { - "safe-buffer": "~5.2.0" + "b4a": "^1.6.4" } }, - "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==", + "node_modules/text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", "dev": true, "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, "engines": { - "node": ">=10" + "node": ">=0.10" } }, - "node_modules/string-range": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/string-range/-/string-range-1.2.2.tgz", - "integrity": "sha512-tYft6IFi8SjplJpxCUxyqisD3b+R2CSkomrtJYCkvuf1KuCAWgz7YXt4O0jip7efpfCemwHEzTEAO8EuOYgh3w==", + "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/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==", - "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/string-width-cjs": { - "name": "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==", + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "devOptional": true, "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" + "any-promise": "^1.0.0" } }, - "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==", + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "devOptional": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "thenify": ">= 3.1.0 < 4" }, "engines": { - "node": ">=8" + "node": ">=0.8" } }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "node_modules/three": { + "version": "0.156.1", + "resolved": "https://registry.npmjs.org/three/-/three-0.156.1.tgz", + "integrity": "sha512-kP7H0FK9d/k6t/XvQ9FO6i+QrePoDcNhwl0I02+wmUJRNSLCUIDMcfObnzQvxb37/0Uc9TDT0T1HgsRRrO6SYQ==", + "license": "MIT" }, - "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, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", "license": "MIT", - "engines": { - "node": ">=8" - } + "optional": true, + "peer": true }, - "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==", + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/strip-hex-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", - "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", "license": "MIT", "dependencies": { - "is-hex-prefixed": "1.0.0" - }, - "engines": { - "node": ">=6.5.0", - "npm": ">=3" + "readable-stream": "3" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, + "node_modules/through2/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "dependencies": { - "min-indent": "^1.0.0" + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" }, "engines": { - "node": ">=8" + "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, + "node_modules/tiny-async-pool": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", + "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "semver": "^5.5.0" } }, - "node_modules/structured-headers": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-0.4.1.tgz", - "integrity": "sha512-0MP/Cxx5SzeeZ10p/bZI0S6MpgD+yxAhi1BOQ34jgnMXsCq3j1t6tQnZu+KdlL7dvJTLT3g9xN8tl10TqgFMcg==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, + "node_modules/tiny-async-pool/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "semver": "bin/semver" } }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "devOptional": true, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "license": "MIT", "engines": { - "node": ">= 6" + "node": ">=14.14" } }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "devOptional": true, - "license": "ISC", + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "tmp": "^0.2.0" } }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "devOptional": true, - "license": "ISC", + "license": "BSD-3-Clause" + }, + "node_modules/to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.4" } }, - "node_modules/sucrase/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "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==", "devOptional": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "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==", "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "is-number": "^7.0.0" }, "engines": { - "node": ">=8" + "node": ">=8.0" } }, - "node_modules/supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "optional": true, "peer": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, "engines": { - "node": ">=8" + "node": ">=0.6" } }, - "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==", - "devOptional": true, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "tree-kill": "cli.js" } }, - "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", - "dev": true, + "node_modules/treeify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", + "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" + "node": ">=0.6" } }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": ">=8" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "license": "ISC", + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "license": "WTFPL", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "utf8-byte-length": "^1.0.1" } }, - "node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "devOptional": true, + "license": "Apache-2.0" }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/ts-jest": { + "version": "29.4.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", + "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", + "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.2", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" }, "engines": { - "node": ">=6" - } - }, - "node_modules/tar-stream/node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/tar-stream/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "@jest/transform": { + "optional": true }, - { - "type": "consulting", - "url": "https://feross.org/support" + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" } }, - "node_modules/tar-stream/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">= 6" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "node_modules/ts-jest/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": ">=8" + "node": ">=12" } }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, "bin": { - "mkdirp": "bin/cmd.js" + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=10" + "node": ">=0.3.1" } }, - "node_modules/temp": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", - "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "mkdirp": "^0.5.1", - "rimraf": "~2.6.2" + "tslib": "^1.8.1" }, "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "node_modules/temp-file": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", - "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", - "license": "MIT", - "dependencies": { - "async-exit-hook": "^2.0.1", - "fs-extra": "^10.0.0" - } + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true, + "license": "0BSD" }, - "node_modules/temp-file/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/tsx": { + "version": "4.20.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz", + "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==", + "dev": true, "license": "MIT", "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "esbuild": "~0.25.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" }, "engines": { - "node": ">=12" + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" } }, - "node_modules/temp/node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "peer": true, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", "dependencies": { - "glob": "^7.1.3" + "safe-buffer": "^5.0.1" }, - "bin": { - "rimraf": "bin.js" + "engines": { + "node": "*" } }, - "node_modules/tempy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", - "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, + "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": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8.0" } }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "dev": true, + "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==", + "devOptional": 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==", + "devOptional": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" @@ -28776,1266 +31649,1165 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "license": "MIT", - "optional": true, - "peer": true, "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.4" } }, - "node_modules/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", - "license": "BSD-2-Clause", - "optional": true, - "peer": true, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/typedarray-to-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-1.0.4.tgz", + "integrity": "sha512-vjMKrfSoUDN8/Vnqitw2FmstOfuJ73G6CrSEKnf11A6RmasVxHqfeBcnTb6RsL4pTMuV5Zsv9IiHRphMZyckUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/typeorm": { + "version": "0.3.26", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.26.tgz", + "integrity": "sha512-o2RrBNn3lczx1qv4j+JliVMmtkPSqEGpG0UuZkt9tCfWkoXKu8MZnjvp2GjWPll1SehwemQw6xrbVRhmOglj8Q==", + "license": "MIT", "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" + "@sqltools/formatter": "^1.2.5", + "ansis": "^3.17.0", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "dayjs": "^1.11.13", + "debug": "^4.4.0", + "dedent": "^1.6.0", + "dotenv": "^16.4.7", + "glob": "^10.4.5", + "sha.js": "^2.4.11", + "sql-highlight": "^6.0.0", + "tslib": "^2.8.1", + "uuid": "^11.1.0", + "yargs": "^17.7.2" }, "bin": { - "terser": "bin/terser" + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" }, "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "license": "MIT", - "optional": true, - "peer": true - }, - "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==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "node": ">=16.13.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", + "@sap/hana-client": "^2.14.22", + "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0 || ^6.0.0", + "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0 || ^5.0.14", + "reflect-metadata": "^0.1.14 || ^0.2.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } } }, - "node_modules/test-exclude/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==", - "devOptional": true, + "node_modules/typeorm/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, + "node_modules/typeorm/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/text-decoder": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", - "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", - "devOptional": true, - "license": "Apache-2.0", + "node_modules/typeorm/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", "dependencies": { - "b4a": "^1.6.4" - } - }, - "node_modules/text-extensions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", - "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", - "dev": true, - "license": "MIT", + "brace-expansion": "^2.0.1" + }, "engines": { - "node": ">=0.10" - } - }, - "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/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, + "node_modules/typeorm/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", "engines": { - "node": ">=0.8" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/three": { - "version": "0.156.1", - "resolved": "https://registry.npmjs.org/three/-/three-0.156.1.tgz", - "integrity": "sha512-kP7H0FK9d/k6t/XvQ9FO6i+QrePoDcNhwl0I02+wmUJRNSLCUIDMcfObnzQvxb37/0Uc9TDT0T1HgsRRrO6SYQ==", - "license": "MIT" - }, - "node_modules/throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "license": "MIT", - "optional": true, - "peer": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, + "node_modules/typeorm/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/through2": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", - "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", - "license": "MIT", - "dependencies": { - "readable-stream": "3" - } - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } + "node_modules/typeorm/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, - "node_modules/tiny-async-pool": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tiny-async-pool/-/tiny-async-pool-1.3.0.tgz", - "integrity": "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==", + "node_modules/typeorm/node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", - "dependencies": { - "semver": "^5.5.0" - } - }, - "node_modules/tiny-async-pool/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "license": "ISC", "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tmp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", - "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", - "license": "MIT", - "engines": { - "node": ">=14.14" + "uuid": "dist/esm/bin/uuid" } }, - "node_modules/tmp-promise": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", - "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "node_modules/types-ramda": { + "version": "0.29.10", + "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.10.tgz", + "integrity": "sha512-5PJiW/eiTPyXXBYGZOYGezMl6qj7keBiZheRwfjJZY26QPHsNrjfJnz0mru6oeqqoTHOni893Jfd6zyUXfQRWg==", + "dev": true, "license": "MIT", "dependencies": { - "tmp": "^0.2.0" + "ts-toolbelt": "^9.6.0" } }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "node_modules/typescript": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "devOptional": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-buffer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", - "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", - "license": "MIT", - "dependencies": { - "isarray": "^2.0.5", - "safe-buffer": "^5.2.1", - "typed-array-buffer": "^1.0.3" + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": ">= 0.4" + "node": ">=14.17" } }, - "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==", - "devOptional": true, + "node_modules/ua-parser-js": { + "version": "1.0.41", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.41.tgz", + "integrity": "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" + "bin": { + "ua-parser-js": "script/cli.js" }, "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=0.6" + "node": "*" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, "license": "MIT" }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "license": "MIT", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/treeify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/treeify/-/treeify-1.1.0.tgz", - "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", - "license": "MIT", + "uglifyjs": "bin/uglifyjs" + }, "engines": { - "node": ">=0.6" + "node": ">=0.8.0" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" + "node_modules/uint8-varint": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.4.tgz", + "integrity": "sha512-FwpTa7ZGA/f/EssWAb5/YV6pHgVF1fViKdW8cWaEarjB8t7NyofSWBdOTyFPaGuUG4gx3v1O3PQ8etsiOs3lcw==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "uint8arraylist": "^2.0.0", + "uint8arrays": "^5.0.0" } }, - "node_modules/truncate-utf8-bytes": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", - "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", - "license": "WTFPL", + "node_modules/uint8arraylist": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/uint8arraylist/-/uint8arraylist-2.4.8.tgz", + "integrity": "sha512-vc1PlGOzglLF0eae1M8mLRTBivsvrGsdmJ5RbK3e+QRvRLOZfZhQROTwH/OfyF3+ZVUg9/8hE8bmKP2CvP9quQ==", + "license": "Apache-2.0 OR MIT", "dependencies": { - "utf8-byte-length": "^1.0.1" + "uint8arrays": "^5.0.1" } }, - "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" + "node_modules/uint8arrays": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.0.tgz", + "integrity": "sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==", + "license": "Apache-2.0 OR MIT", + "dependencies": { + "multiformats": "^13.0.0" } }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "devOptional": true, - "license": "Apache-2.0" + "node_modules/uint8arrays/node_modules/multiformats": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.0.tgz", + "integrity": "sha512-Mkb/QcclrJxKC+vrcIFl297h52QcKh2Az/9A5vbWytbQt4225UWWWmIuSsKksdww9NkIeYcA7DkfftyLuC/JSg==", + "license": "Apache-2.0 OR MIT" }, - "node_modules/ts-jest": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", - "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", - "dev": true, + "node_modules/undici": { + "version": "6.21.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", + "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, + "optional": true, + "peer": true, "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } + "node": ">=18.17" } }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" }, - "node_modules/ts-jest/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", + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "optional": true, + "peer": true, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "license": "MIT", + "optional": true, + "peer": true, "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, - "license": "BSD-3-Clause", + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, "engines": { - "node": ">=0.3.1" + "node": ">=4" } }, - "node_modules/ts-toolbelt": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", - "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", - "dev": true, - "license": "Apache-2.0" + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "license": "0BSD" + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">=4" + } }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true, "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, "engines": { - "node": ">= 6" + "node": ">=18" }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", + "node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "license": "ISC", "dependencies": { - "safe-buffer": "^5.0.1" + "unique-slug": "^3.0.0" }, "engines": { - "node": "*" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", - "license": "Unlicense" - }, - "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", + "node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "license": "ISC", "dependencies": { - "prelude-ls": "^1.2.1" + "imurmurhash": "^0.1.4" }, "engines": { - "node": ">= 0.8.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.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==", + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "devOptional": true, "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "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==", - "devOptional": true, - "license": "(MIT OR CC0-1.0)", + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 10.0.0" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, + "optional": true, + "peer": true, "engines": { - "node": ">= 0.4" + "node": ">= 0.8" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/typedarray-to-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-1.0.4.tgz", - "integrity": "sha512-vjMKrfSoUDN8/Vnqitw2FmstOfuJ73G6CrSEKnf11A6RmasVxHqfeBcnTb6RsL4pTMuV5Zsv9IiHRphMZyckUw==", + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", "dev": true, - "license": "MIT" - }, - "node_modules/typeorm": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.25.tgz", - "integrity": "sha512-fTKDFzWXKwAaBdEMU4k661seZewbNYET4r1J/z3Jwf+eAvlzMVpTLKAVcAzg75WwQk7GDmtsmkZ5MfkmXCiFWg==", + "hasInstallScript": true, "license": "MIT", "dependencies": { - "@sqltools/formatter": "^1.2.5", - "ansis": "^3.17.0", - "app-root-path": "^3.1.0", - "buffer": "^6.0.3", - "dayjs": "^1.11.13", - "debug": "^4.4.0", - "dedent": "^1.6.0", - "dotenv": "^16.4.7", - "glob": "^10.4.5", - "sha.js": "^2.4.11", - "sql-highlight": "^6.0.0", - "tslib": "^2.8.1", - "uuid": "^11.1.0", - "yargs": "^17.7.2" - }, - "bin": { - "typeorm": "cli.js", - "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", - "typeorm-ts-node-esm": "cli-ts-node-esm.js" - }, - "engines": { - "node": ">=16.13.0" + "napi-postinstall": "^0.3.0" }, "funding": { - "url": "https://opencollective.com/typeorm" - }, - "peerDependencies": { - "@google-cloud/spanner": "^5.18.0 || ^6.0.0 || ^7.0.0", - "@sap/hana-client": "^2.12.25", - "better-sqlite3": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", - "hdb-pool": "^0.1.6", - "ioredis": "^5.0.4", - "mongodb": "^5.8.0 || ^6.0.0", - "mssql": "^9.1.1 || ^10.0.1 || ^11.0.1", - "mysql2": "^2.2.5 || ^3.0.1", - "oracledb": "^6.3.0", - "pg": "^8.5.1", - "pg-native": "^3.0.0", - "pg-query-stream": "^4.0.0", - "redis": "^3.1.1 || ^4.0.0", - "reflect-metadata": "^0.1.14 || ^0.2.0", - "sql.js": "^1.4.0", - "sqlite3": "^5.0.3", - "ts-node": "^10.7.0", - "typeorm-aurora-data-api-driver": "^2.0.0 || ^3.0.0" + "url": "https://opencollective.com/unrs-resolver" }, - "peerDependenciesMeta": { - "@google-cloud/spanner": { - "optional": true - }, - "@sap/hana-client": { - "optional": true - }, - "better-sqlite3": { - "optional": true - }, - "hdb-pool": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "mongodb": { - "optional": true - }, - "mssql": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-native": { - "optional": true - }, - "pg-query-stream": { - "optional": true - }, - "redis": { - "optional": true - }, - "sql.js": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "ts-node": { - "optional": true - }, - "typeorm-aurora-data-api-driver": { - "optional": true - } + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, - "node_modules/typeorm/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/typeorm/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", + "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==", + "devOptional": 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": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { - "glob": "dist/esm/bin.mjs" + "update-browserslist-db": "cli.js" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "node_modules/typeorm/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", + "node_modules/update-check": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", + "dev": true, + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0" } }, - "node_modules/typeorm/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "node_modules/typeorm/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==", + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", "license": "MIT" }, - "node_modules/typeorm/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "license": "(WTFPL OR MIT)" }, - "node_modules/typeorm/node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "license": "MIT", "bin": { - "uuid": "dist/esm/bin/uuid" + "uuid": "dist/bin/uuid" } }, - "node_modules/types-ramda": { - "version": "0.29.10", - "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.10.tgz", - "integrity": "sha512-5PJiW/eiTPyXXBYGZOYGezMl6qj7keBiZheRwfjJZY26QPHsNrjfJnz0mru6oeqqoTHOni893Jfd6zyUXfQRWg==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true, + "license": "MIT" + }, + "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": "MIT", + "license": "ISC", "dependencies": { - "ts-toolbelt": "^9.6.0" + "@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/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "devOptional": true, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", + "license": "ISC", + "optional": true, + "peer": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/validator": { + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/vite": { + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ua-parser-js": { - "version": "1.0.40", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.40.tgz", - "integrity": "sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" + "peerDependenciesMeta": { + "@types/node": { + "optional": true }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" + "less": { + "optional": true }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" ], + "dev": true, "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, + "optional": true, + "os": [ + "aix" + ], "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "dev": true, - "license": "MIT" - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, + "os": [ + "android" + ], "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uint8-varint": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/uint8-varint/-/uint8-varint-2.0.4.tgz", - "integrity": "sha512-FwpTa7ZGA/f/EssWAb5/YV6pHgVF1fViKdW8cWaEarjB8t7NyofSWBdOTyFPaGuUG4gx3v1O3PQ8etsiOs3lcw==", - "license": "Apache-2.0 OR MIT", - "dependencies": { - "uint8arraylist": "^2.0.0", - "uint8arrays": "^5.0.0" - } - }, - "node_modules/uint8arraylist": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/uint8arraylist/-/uint8arraylist-2.4.8.tgz", - "integrity": "sha512-vc1PlGOzglLF0eae1M8mLRTBivsvrGsdmJ5RbK3e+QRvRLOZfZhQROTwH/OfyF3+ZVUg9/8hE8bmKP2CvP9quQ==", - "license": "Apache-2.0 OR MIT", - "dependencies": { - "uint8arrays": "^5.0.1" - } - }, - "node_modules/uint8arrays": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-5.1.0.tgz", - "integrity": "sha512-vA6nFepEmlSKkMBnLBaUMVvAC4G3CTmO58C12y4sq6WPDOR7mOFYOi7GlrQ4djeSbP6JG9Pv9tJDM97PedRSww==", - "license": "Apache-2.0 OR MIT", - "dependencies": { - "multiformats": "^13.0.0" + "node": ">=12" } }, - "node_modules/uint8arrays/node_modules/multiformats": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.4.0.tgz", - "integrity": "sha512-Mkb/QcclrJxKC+vrcIFl297h52QcKh2Az/9A5vbWytbQt4225UWWWmIuSsKksdww9NkIeYcA7DkfftyLuC/JSg==", - "license": "Apache-2.0 OR MIT" - }, - "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "android" + ], "engines": { - "node": ">=18.17" + "node": ">=12" } }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", - "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "android" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=4" + "node": ">=12" } }, - "node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "license": "ISC", - "dependencies": { - "unique-slug": "^3.0.0" - }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=12" } }, - "node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4" - }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=12" } }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "devOptional": true, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "crypto-random-string": "^2.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 10.0.0" + "node": ">=12" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/unrs-resolver": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", - "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], "dev": true, - "hasInstallScript": true, "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.3.0" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.11.1", - "@unrs/resolver-binding-android-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-arm64": "1.11.1", - "@unrs/resolver-binding-darwin-x64": "1.11.1", - "@unrs/resolver-binding-freebsd-x64": "1.11.1", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", - "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", - "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", - "@unrs/resolver-binding-linux-x64-musl": "1.11.1", - "@unrs/resolver-binding-wasm32-wasi": "1.11.1", - "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", - "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", - "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "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==", - "devOptional": 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" - } + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" ], + "dev": true, "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" } }, - "node_modules/utf8": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", - "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", - "license": "MIT" - }, - "node_modules/utf8-byte-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", - "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "license": "(WTFPL OR MIT)" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4.0" + "node": ">=12" } }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" ], + "dev": true, "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" } }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true, - "license": "MIT" - }, - "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==", + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], "engines": { - "node": ">=10.12.0" + "node": ">=12" } }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "Apache-2.0", - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", - "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", - "license": "ISC", + "license": "MIT", "optional": true, - "peer": true, + "os": [ + "sunos" + ], "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=12" } }, - "node_modules/validator": { - "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.10" + "node": ">=12" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, "license": "MIT", "optional": true, - "peer": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/verror": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", - "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - }, + "os": [ + "win32" + ], "engines": { - "node": ">=0.6.0" + "node": ">=12" } }, - "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, "bin": { - "vite": "bin/vite.js" + "esbuild": "bin/esbuild" }, "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" + "node": ">=12" }, "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/vlq": { @@ -30047,16 +32819,16 @@ "peer": true }, "node_modules/vue": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", - "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.18", - "@vue/compiler-sfc": "3.5.18", - "@vue/runtime-dom": "3.5.18", - "@vue/server-renderer": "3.5.18", - "@vue/shared": "3.5.18" + "@vue/compiler-dom": "3.5.13", + "@vue/compiler-sfc": "3.5.13", + "@vue/runtime-dom": "3.5.13", + "@vue/server-renderer": "3.5.13", + "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" @@ -30077,33 +32849,6 @@ "vue": "^3.0.0 || ^2.0.0" } }, - "node_modules/vue-demi": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", - "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", - "hasInstallScript": true, - "license": "MIT", - "peer": true, - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, "node_modules/vue-eslint-parser": { "version": "9.4.3", "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", @@ -30407,6 +33152,76 @@ "string-width": "^1.0.2 || 2 || 3 || 4" } }, + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", + "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/widest-line/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/wonka": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/wonka/-/wonka-6.3.5.tgz", diff --git a/package.json b/package.json index 7b27258d..9ca11deb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "timesafari", - "version": "1.0.7-beta", + "version": "1.1.0-beta", "description": "Time Safari Application", "author": { "name": "Time Safari Team" @@ -8,9 +8,12 @@ "scripts": { "lint": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src", "lint-fix": "eslint --ext .js,.ts,.vue --ignore-path .gitignore --fix src", + "type-safety-check": "./scripts/type-safety-check.sh", "type-check": "tsc --noEmit", "prebuild": "eslint --ext .js,.ts,.vue --ignore-path .gitignore src && node sw_combine.js && node scripts/copy-wasm.js", "test:prerequisites": "node scripts/check-prerequisites.js", + "check:dependencies": "./scripts/check-dependencies.sh", + "test:all": "npm run lint && tsc && npm run test:web && npm run test:mobile && ./scripts/test-safety-check.sh && echo '\n\n\nGotta add the performance tests'", "test:web": "npx playwright test -c playwright.config-local.ts --trace on", "test:mobile": "./scripts/test-mobile.sh", "test:android": "node scripts/test-android.js", @@ -25,6 +28,11 @@ "auto-run:electron": "./scripts/auto-run.sh --platform=electron", "build:capacitor": "VITE_GIT_HASH=`git log -1 --pretty=format:%h` vite build --mode capacitor --config vite.config.capacitor.mts", "build:capacitor:sync": "npm run build:capacitor && npx cap sync", + "build:native": "vite build && npx cap sync && npx capacitor-assets generate", + "assets:config": "npx tsx scripts/assets-config.ts", + "assets:validate": "npx tsx scripts/assets-validator.ts", + "assets:validate:android": "./scripts/build-android.sh --assets-only", + "assets:clean": "rimraf android/app/src/main/res/mipmap-* ios/App/App/Assets.xcassets/**/AppIcon*.png ios/App/App/Assets.xcassets/**/Splash*.png || true", "build:ios": "./scripts/build-ios.sh", "build:ios:dev": "./scripts/build-ios.sh --dev", "build:ios:test": "./scripts/build-ios.sh --test", @@ -53,6 +61,8 @@ "build:web:docker:test": "./scripts/build-web.sh --docker:test", "build:web:docker:prod": "./scripts/build-web.sh --docker:prod", "build:web:serve": "./scripts/build-web.sh --serve", + "build:web:serve:test": "./scripts/build-web.sh --serve --test", + "build:web:serve:prod": "./scripts/build-web.sh --serve --prod", "docker:up": "docker-compose up", "docker:up:test": "npm run build:web:build -- --mode test && docker-compose up test", "docker:up:prod": "npm run build:web:build -- --mode production && docker-compose up production", @@ -89,7 +99,14 @@ "build:electron:dmg:dev": "./scripts/build-electron.sh --dev --dmg", "build:electron:dmg:test": "./scripts/build-electron.sh --test --dmg", "build:electron:dmg:prod": "./scripts/build-electron.sh --prod --dmg", - "clean:android": "adb uninstall app.timesafari.app || true", + "markdown:fix": "markdownlint-cli2 --fix", + "markdown:check": "markdownlint-cli2", + "markdown:setup": "./scripts/setup-markdown-hooks.sh", + "prepare": "husky", + "guard": "bash ./scripts/build-arch-guard.sh", + "guard:test": "bash ./scripts/build-arch-guard.sh --staged", + "guard:setup": "npm run prepare && echo '✅ Build Architecture Guard is now active!'", + "clean:android": "./scripts/clean-android.sh", "clean:ios": "rm -rf ios/App/build ios/App/Pods ios/App/output ios/App/App/public ios/DerivedData ios/capacitor-cordova-ios-plugins ios/App/App/capacitor.config.json ios/App/App/config.xml || true", "clean:electron": "./scripts/build-electron.sh --clean", "clean:all": "npm run clean:ios && npm run clean:android && npm run clean:electron", @@ -115,6 +132,11 @@ "build:android:dev:run:custom": "./scripts/build-android.sh --dev --api-ip --auto-run", "build:android:test:run:custom": "./scripts/build-android.sh --test --api-ip --auto-run" }, + "lint-staged": { + "*.{js,ts,vue,css,json,yml,yaml}": "eslint --fix || true", + "*.{md,markdown,mdc}": "markdownlint-cli2 --fix" + }, + "dependencies": { "@capacitor-community/electron": "^5.0.1", "@capacitor-community/sqlite": "6.0.2", @@ -123,10 +145,12 @@ "@capacitor/app": "^6.0.0", "@capacitor/camera": "^6.0.0", "@capacitor/cli": "^6.2.0", + "@capacitor/clipboard": "^6.0.2", "@capacitor/core": "^6.2.0", "@capacitor/filesystem": "^6.0.0", "@capacitor/ios": "^6.2.0", "@capacitor/share": "^6.0.3", + "@capacitor/status-bar": "^6.0.2", "@capawesome/capacitor-file-picker": "^6.2.0", "@dicebear/collection": "^5.4.1", "@dicebear/core": "^5.4.1", @@ -193,9 +217,9 @@ "three": "^0.156.1", "ua-parser-js": "^1.0.37", "uint8arrays": "^5.0.0", - "vue": "^3.5.13", + "vue": "3.5.13", "vue-axios": "^3.5.2", - "vue-facing-decorator": "^3.0.4", + "vue-facing-decorator": "3.0.4", "vue-picture-cropper": "^0.7.0", "vue-qrcode-reader": "^5.5.3", "vue-router": "^4.5.0", @@ -204,6 +228,8 @@ }, "devDependencies": { "@capacitor/assets": "^3.0.5", + "@commitlint/cli": "^18.6.1", + "@commitlint/config-conventional": "^18.6.2", "@playwright/test": "^1.54.2", "@types/dom-webcodecs": "^0.1.7", "@types/jest": "^30.0.0", @@ -231,16 +257,21 @@ "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-vue": "^9.32.0", "fs-extra": "^11.3.0", + "husky": "^9.0.11", "jest": "^30.0.4", + "lint-staged": "^15.2.2", "markdownlint": "^0.37.4", "markdownlint-cli": "^0.44.0", + "markdownlint-cli2": "^0.18.1", "npm-check-updates": "^17.1.13", "path-browserify": "^1.0.1", "postcss": "^8.4.38", "prettier": "^3.2.5", "rimraf": "^6.0.1", + "serve": "^14.2.4", "tailwindcss": "^3.4.1", "ts-jest": "^29.4.0", + "tsx": "^4.20.4", "typescript": "~5.2.2", "vite": "^5.2.0" } diff --git a/playwright.config-local.ts b/playwright.config-local.ts index c0e78047..32b7f023 100644 --- a/playwright.config-local.ts +++ b/playwright.config-local.ts @@ -21,7 +21,7 @@ export default defineConfig({ /* Retry on CI only */ retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ - workers: isLinuxEnvironment() ? 4 : undefined, + workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: [ ['list'], diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 00000000..8739bdbd --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,47 @@ +# Build Architecture Guard PR Template + +## Change Level + +- [ ] Level: **L1** / **L2** / **L3** (pick one) + +**Why:** … + +## Scope & Impact + +- [ ] Files & platforms touched: … +- [ ] Risk triggers (env / script flow / packaging / SW+WASM / + Docker / signing): … +- [ ] Mitigations/validation done: … + +## Commands Run (paste exact logs/snips) + +- [ ] Web: `npm run build:web` / `:prod` +- [ ] Electron: `npm run build:electron:dev` / package step +- [ ] Mobile: `npm run build:android:test` / iOS equivalent +- [ ] Clean/auto-run impacted scripts + +## Artifacts + +- [ ] Names + **sha256** of artifacts/installers: + +Artifacts: + +```text +<name-1> <sha256-1> +<name-2> <sha256-2> +``` + +## Docs + +- [ ] **BUILDING.md** updated (sections): … +- [ ] Troubleshooting updated (if applicable) + +## Rollback + +- [ ] Verified steps (1–3 cmds) to restore previous behavior + +## L3 only + +- [ ] ADR link: + +ADR: https://… diff --git a/resources/README.md b/resources/README.md index a3b9f803..d8290cd3 100644 --- a/resources/README.md +++ b/resources/README.md @@ -27,12 +27,14 @@ resources/ ## Asset Requirements ### Icon Requirements + - **Format**: PNG - **Size**: 1024x1024 pixels minimum - **Background**: Transparent or solid color - **Content**: App logo/icon ### Splash Screen Requirements + - **Format**: PNG - **Size**: 1242x2688 pixels (iPhone 11 Pro Max size) - **Background**: Solid color or gradient @@ -70,10 +72,11 @@ Asset generation is configured in `capacitor-assets.config.json` at the project ## Build Integration Assets are automatically generated as part of the build process: + - `npm run build:android` - Generates Android assets - `npm run build:ios` - Generates iOS assets - `npm run build:web` - Generates web assets **Author**: Matthew Raymer **Date**: 2025-01-27 -**Status**: ✅ **ACTIVE** - Asset management system implemented \ No newline at end of file +**Status**: ✅ **ACTIVE** - Asset management system implemented diff --git a/assets/icon.png b/resources/icon.png similarity index 100% rename from assets/icon.png rename to resources/icon.png diff --git a/assets/splash.png b/resources/splash.png similarity index 100% rename from assets/splash.png rename to resources/splash.png diff --git a/assets/splash_dark.png b/resources/splash_dark.png similarity index 100% rename from assets/splash_dark.png rename to resources/splash_dark.png diff --git a/scripts/README.md b/scripts/README.md index 71f61de4..f5aaf629 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -31,6 +31,7 @@ All scripts automatically handle environment variables for different build types #### Automatic Environment Setup Each script automatically: + 1. **Sets platform-specific variables** based on build type 2. **Gets git hash** for versioning (`VITE_GIT_HASH`) 3. **Creates application directories** (`~/.local/share/TimeSafari/timesafari`) @@ -104,6 +105,7 @@ exit 0 ## Benefits of Unification ### Before (Redundant) + ```bash # Each script had 50+ lines of duplicate code: readonly RED='\033[0;31m' @@ -121,6 +123,7 @@ export VITE_PWA_ENABLED=false ``` ### After (Unified) + ```bash # Each script is now ~20 lines of focused logic: source "$(dirname "$0")/common.sh" @@ -133,6 +136,7 @@ print_footer "Script Title" ## Usage Examples ### Running Tests + ```bash # Run all tests ./scripts/test-all.sh @@ -189,6 +193,7 @@ export NODE_ENV=production ``` ### .env File Support + Scripts automatically load variables from `.env` files if they exist: ```bash @@ -199,6 +204,7 @@ CUSTOM_VAR=value ``` ### Environment Validation + Required environment variables can be validated: ```bash @@ -207,6 +213,7 @@ validate_env_vars "VITE_API_URL" "VITE_DEBUG" || exit 1 ``` ### Environment Inspection + View current environment variables with the `--env` flag: ```bash @@ -277,4 +284,4 @@ To verify the common utilities work correctly: - Timing information is automatically collected for all operations - Build artifacts are cleaned up automatically - No redundant command execution or file operations -- Environment variables are set efficiently with minimal overhead \ No newline at end of file +- Environment variables are set efficiently with minimal overhead diff --git a/scripts/assets-config.ts b/scripts/assets-config.ts new file mode 100644 index 00000000..baaadce5 --- /dev/null +++ b/scripts/assets-config.ts @@ -0,0 +1,224 @@ +#!/usr/bin/env tsx + +/** + * TimeSafari Asset Configuration Generator + * Generates capacitor-assets configuration files with deterministic outputs + * Author: Matthew Raymer + * + * Usage: tsx scripts/assets-config.ts + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const PROJECT_ROOT = path.dirname(__dirname); + +// TypeScript interfaces for asset configuration +interface AdaptiveIconConfig { + foreground: string; + background: string; + monochrome: string; +} + +interface AndroidIconConfig { + adaptive: AdaptiveIconConfig; + target: string; +} + +interface IOSIconConfig { + padding: number; + target: string; +} + +interface WebIconConfig { + target: string; +} + +interface IconConfig { + source: string; + android: AndroidIconConfig; + ios: IOSIconConfig; + web: WebIconConfig; +} + +interface AndroidSplashConfig { + scale: string; + target: string; +} + +interface IOSSplashConfig { + useStoryBoard: boolean; + target: string; +} + +interface SplashConfig { + source: string; + darkSource: string; + android: AndroidSplashConfig; + ios: IOSSplashConfig; +} + +interface AssetConfig { + icon: IconConfig; + splash: SplashConfig; +} + +/** + * Generate deterministic capacitor-assets configuration + * @returns Sorted, stable configuration object + */ +function generateAssetConfig(): AssetConfig { + const config: AssetConfig = { + icon: { + source: "resources/icon.png", + android: { + adaptive: { + foreground: "resources/icon.png", + background: "#121212", + monochrome: "resources/icon.png" + }, + target: "android/app/src/main/res" + }, + ios: { + padding: 0, + target: "ios/App/App/Assets.xcassets/AppIcon.appiconset" + }, + web: { + target: "public/img/icons" + } + }, + splash: { + source: "resources/splash.png", + darkSource: "resources/splash_dark.png", + android: { + scale: "cover", + target: "android/app/src/main/res" + }, + ios: { + useStoryBoard: true, + target: "ios/App/App/Assets.xcassets" + } + } + }; + + return sortObjectKeys(config); +} + +/** + * Sort object keys recursively for deterministic output + * @param obj - Object to sort + * @returns Object with sorted keys + */ +function sortObjectKeys(obj: any): any { + if (obj === null || typeof obj !== 'object') { + return obj; + } + + if (Array.isArray(obj)) { + return obj.map(sortObjectKeys); + } + + const sorted: any = {}; + Object.keys(obj) + .sort() + .forEach(key => { + sorted[key] = sortObjectKeys(obj[key]); + }); + + return sorted; +} + +/** + * Validate that required source files exist + */ +function validateSourceFiles(): void { + const requiredFiles = [ + 'resources/icon.png', + 'resources/splash.png', + 'resources/splash_dark.png' + ]; + + const missingFiles = requiredFiles.filter(file => { + const filePath = path.join(PROJECT_ROOT, file); + return !fs.existsSync(filePath); + }); + + if (missingFiles.length > 0) { + console.error('❌ Missing required source files:'); + missingFiles.forEach(file => console.error(` ${file}`)); + process.exit(1); + } + + console.log('✅ All required source files found'); +} + +/** + * Write configuration to file with consistent formatting + * @param config - Configuration object + * @param outputPath - Output file path + */ +function writeConfig(config: AssetConfig, outputPath: string): void { + const jsonString = JSON.stringify(config, null, 2); + + // Ensure consistent line endings and no trailing whitespace + const cleanJson = jsonString + .split('\n') + .map(line => line.trimEnd()) + .join('\n') + '\n'; + + fs.writeFileSync(outputPath, cleanJson, 'utf8'); + console.log(`✅ Configuration written to: ${outputPath}`); +} + +/** + * Main execution function + */ +function main(): void { + console.log('🔄 Generating TimeSafari asset configuration...'); + console.log(`📁 Project root: ${PROJECT_ROOT}`); + console.log(`📅 Generated: ${new Date().toISOString()}`); + + try { + // Validate source files exist + validateSourceFiles(); + + // Generate configuration + const config = generateAssetConfig(); + + // Ensure config directory exists + const configDir = path.join(PROJECT_ROOT, 'config', 'assets'); + if (!fs.existsSync(configDir)) { + fs.mkdirSync(configDir, { recursive: true }); + } + + // Write configuration files + const capacitorAssetsConfigPath = path.join(configDir, 'capacitor-assets.config.json'); + writeConfig(config, capacitorAssetsConfigPath); + + // Copy to root for capacitor-assets discovery + const rootConfigPath = path.join(PROJECT_ROOT, 'capacitor-assets.config.json'); + writeConfig(config, rootConfigPath); + + console.log('🎉 Asset configuration generation completed successfully!'); + console.log(''); + console.log('📋 Next steps:'); + console.log(' 1. Review the generated configuration'); + console.log(' 2. Commit the configuration files'); + console.log(' 3. Run "npm run assets:validate" to verify'); + console.log(' 4. Use "npm run build:native" for builds'); + + } catch (error) { + console.error('❌ Configuration generation failed:', error instanceof Error ? error.message : String(error)); + process.exit(1); + } +} + +// Run if called directly +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +} + +export { generateAssetConfig, sortObjectKeys, validateSourceFiles, AssetConfig }; diff --git a/scripts/assets-validator.ts b/scripts/assets-validator.ts new file mode 100644 index 00000000..587a25c7 --- /dev/null +++ b/scripts/assets-validator.ts @@ -0,0 +1,239 @@ +#!/usr/bin/env tsx + +/** + * TimeSafari Asset Configuration Validator + * Validates capacitor-assets configuration against schema and source files + * Author: Matthew Raymer + * + * Usage: tsx scripts/assets-validator.ts [config-path] + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const PROJECT_ROOT = path.dirname(__dirname); + +// TypeScript interfaces for validation +interface ValidationError { + message: string; +} + +interface AssetConfig { + icon?: { + source?: string; + android?: { + adaptive?: { + foreground?: string; + background?: string; + monochrome?: string; + }; + }; + }; + splash?: { + source?: string; + darkSource?: string; + }; +} + +/** + * Load and parse JSON file + * @param filePath - Path to JSON file + * @returns Parsed JSON object + */ +function loadJsonFile(filePath: string): AssetConfig { + try { + const content = fs.readFileSync(filePath, 'utf8'); + return JSON.parse(content); + } catch (error) { + throw new Error(`Failed to load ${filePath}: ${error instanceof Error ? error.message : String(error)}`); + } +} + +/** + * Validate configuration against schema + * @param config - Configuration object to validate + * @returns Array of validation errors + */ +function validateAgainstSchema(config: AssetConfig): ValidationError[] { + const errors: ValidationError[] = []; + + // Basic structure validation + if (!config.icon || !config.splash) { + errors.push({ message: 'Configuration must contain both "icon" and "splash" sections' }); + } + + // Icon validation + if (config.icon) { + if (!config.icon.source) { + errors.push({ message: 'Icon section must contain "source" field' }); + } else if (!/^resources\/.*\.(png|svg)$/.test(config.icon.source)) { + errors.push({ message: 'Icon source must be a PNG or SVG file in resources/ directory' }); + } + + // Android adaptive icon validation + if (config.icon.android?.adaptive) { + const adaptive = config.icon.android.adaptive; + if (!adaptive.foreground || !adaptive.background) { + errors.push({ message: 'Android adaptive icon must have both foreground and background' }); + } + if (adaptive.foreground && !/^resources\/.*\.(png|svg)$/.test(adaptive.foreground)) { + errors.push({ message: 'Android adaptive foreground must be a PNG or SVG file in resources/ directory' }); + } + } + } + + // Splash validation + if (config.splash) { + if (!config.splash.source) { + errors.push({ message: 'Splash section must contain "source" field' }); + } else if (!/^resources\/.*\.(png|svg)$/.test(config.splash.source)) { + errors.push({ message: 'Splash source must be a PNG or SVG file in resources/ directory' }); + } + + if (config.splash.darkSource && !/^resources\/.*\.(png|svg)$/.test(config.splash.darkSource)) { + errors.push({ message: 'Dark splash source must be a PNG or SVG file in resources/ directory' }); + } + } + + return errors; +} + +/** + * Validate that source files exist + * @param config - Configuration object + * @returns Array of missing file errors + */ +function validateSourceFiles(config: AssetConfig): ValidationError[] { + const errors: ValidationError[] = []; + const requiredFiles = new Set<string>(); + + // Collect all required source files + if (config.icon?.source) requiredFiles.add(config.icon.source); + if (config.icon?.android?.adaptive?.foreground) requiredFiles.add(config.icon.android.adaptive.foreground); + if (config.icon?.android?.adaptive?.monochrome) requiredFiles.add(config.icon.android.adaptive.monochrome); + if (config.splash?.source) requiredFiles.add(config.splash.source); + if (config.splash?.darkSource) requiredFiles.add(config.splash.darkSource); + + // Check each file exists + requiredFiles.forEach(file => { + const filePath = path.join(PROJECT_ROOT, file); + if (!fs.existsSync(filePath)) { + errors.push({ message: `Source file not found: ${file}` }); + } + }); + + return errors; +} + +/** + * Validate target directories are writable + * @param config - Configuration object + * @returns Array of directory validation errors + */ +function validateTargetDirectories(config: AssetConfig): ValidationError[] { + const errors: ValidationError[] = []; + const targetDirs = new Set<string>(); + + // Collect all target directories + if (config.icon?.android?.target) targetDirs.add(config.icon.android.target); + if (config.icon?.ios?.target) targetDirs.add(config.icon.ios.target); + if (config.icon?.web?.target) targetDirs.add(config.icon.web.target); + if (config.splash?.android?.target) targetDirs.add(config.splash.android.target); + if (config.splash?.ios?.target) targetDirs.add(config.splash.ios.target); + + // Check each target directory + targetDirs.forEach(dir => { + const dirPath = path.join(PROJECT_ROOT, dir); + const parentDir = path.dirname(dirPath); + + if (!fs.existsSync(parentDir)) { + errors.push({ message: `Parent directory does not exist: ${parentDir}` }); + } else if (!fs.statSync(parentDir).isDirectory()) { + errors.push({ message: `Parent path is not a directory: ${parentDir}` }); + } + }); + + return errors; +} + +/** + * Main validation function + * @param configPath - Path to configuration file + * @returns True if validation passes + */ +function validateConfiguration(configPath: string): boolean { + console.log('🔍 Validating TimeSafari asset configuration...'); + console.log(`📁 Config file: ${configPath}`); + console.log(`📁 Project root: ${PROJECT_ROOT}`); + + try { + // Load configuration + const config = loadJsonFile(configPath); + console.log('✅ Configuration file loaded successfully'); + + // Load schema + const schemaPath = path.join(PROJECT_ROOT, 'config', 'assets', 'schema.json'); + const schema = loadJsonFile(schemaPath); + console.log('✅ Schema file loaded successfully'); + + // Perform validations + const schemaErrors = validateAgainstSchema(config); + const fileErrors = validateSourceFiles(config); + const dirErrors = validateTargetDirectories(config); + + // Report results + const allErrors = [...schemaErrors, ...fileErrors, ...dirErrors]; + + if (allErrors.length === 0) { + console.log('🎉 All validations passed successfully!'); + console.log(''); + console.log('📋 Configuration summary:'); + console.log(` Icon source: ${config.icon?.source || 'NOT SET'}`); + console.log(` Splash source: ${config.splash?.source || 'NOT SET'}`); + console.log(` Dark splash: ${config.splash?.darkSource || 'NOT SET'}`); + console.log(` Android adaptive: ${config.icon?.android?.adaptive ? 'ENABLED' : 'DISABLED'}`); + console.log(` iOS LaunchScreen: ${config.splash?.ios?.useStoryBoard ? 'ENABLED' : 'DISABLED'}`); + return true; + } else { + console.error('❌ Validation failed with the following errors:'); + allErrors.forEach((error, index) => { + console.error(` ${index + 1}. ${error.message}`); + }); + return false; + } + + } catch (error) { + console.error('❌ Validation failed:', error instanceof Error ? error.message : String(error)); + return false; + } +} + +/** + * Main execution function + */ +function main(): void { + const configPath = process.argv[2] || path.join(PROJECT_ROOT, 'capacitor-assets.config.json'); + + if (!fs.existsSync(configPath)) { + console.error(`❌ Configuration file not found: ${configPath}`); + console.log(''); + console.log('💡 Available options:'); + console.log(' - Use default: capacitor-assets.config.json'); + console.log(' - Specify path: tsx scripts/assets-validator.ts path/to/config.json'); + console.log(' - Generate config: npm run assets:config'); + process.exit(1); + } + + const success = validateConfiguration(configPath); + process.exit(success ? 0 : 1); +} + +// Run if called directly +if (import.meta.url === `file://${process.argv[1]}`) { + main(); +} + +export { validateConfiguration, validateAgainstSchema, validateSourceFiles, validateTargetDirectories, AssetConfig }; diff --git a/scripts/build-android.sh b/scripts/build-android.sh index 53ada97e..f57f70c7 100755 --- a/scripts/build-android.sh +++ b/scripts/build-android.sh @@ -41,7 +41,7 @@ # 6 - Capacitor sync failed # 7 - Asset generation failed # 8 - Android Studio launch failed -# 9 - Resource check failed +# 9 - Android asset validation failed # Exit on any error set -e @@ -49,6 +49,142 @@ set -e # Source common utilities source "$(dirname "$0")/common.sh" +# Function to validate critical dependencies +validate_dependencies() { + log_info "Validating critical dependencies..." + + # Check if node_modules exists + if [ ! -d "node_modules" ]; then + log_error "node_modules directory not found. Please run 'npm install' first." + exit 1 + fi + + # Check if tsx is available + if [ ! -f "node_modules/.bin/tsx" ]; then + log_error "tsx dependency not found. Please run 'npm install' first." + exit 1 + fi + + # Check if capacitor-assets is available + if [ ! -f "node_modules/.bin/capacitor-assets" ]; then + log_error "capacitor-assets dependency not found. Please run 'npm install' first." + exit 1 + fi + + log_success "All critical dependencies validated successfully" +} + +# Function to validate Android assets and resources +validate_android_assets() { + log_info "Validating Android assets and resources..." + + # Check if source assets exist + local missing_assets=() + + if [ ! -f "resources/icon.png" ]; then + missing_assets+=("resources/icon.png") + fi + + if [ ! -f "resources/splash.png" ]; then + missing_assets+=("resources/splash.png") + fi + + if [ ! -f "resources/splash_dark.png" ]; then + missing_assets+=("resources/splash_dark.png") + fi + + if [ ${#missing_assets[@]} -gt 0 ]; then + log_error "Missing source assets:" + for asset in "${missing_assets[@]}"; do + log_error " - $asset" + done + log_error "Please ensure all required assets are present in the resources/ directory." + return 1 + fi + + # Check if Android drawable resources exist + local missing_drawables=() + + if [ ! -f "android/app/src/main/res/drawable/splash.png" ]; then + missing_drawables+=("drawable/splash.png") + fi + + # Check if mipmap resources exist + local missing_mipmaps=() + local mipmap_dirs=("mipmap-mdpi" "mipmap-hdpi" "mipmap-xhdpi" "mipmap-xxhdpi" "mipmap-xxxhdpi") + + for dir in "${mipmap_dirs[@]}"; do + if [ ! -f "android/app/src/main/res/$dir/ic_launcher.png" ]; then + missing_mipmaps+=("$dir/ic_launcher.png") + fi + if [ ! -f "android/app/src/main/res/$dir/ic_launcher_round.png" ]; then + missing_mipmaps+=("$dir/ic_launcher_round.png") + fi + done + + # If any resources are missing, regenerate them + if [ ${#missing_drawables[@]} -gt 0 ] || [ ${#missing_mipmaps[@]} -gt 0 ]; then + log_warn "Missing Android resources detected:" + for resource in "${missing_drawables[@]}" "${missing_mipmaps[@]}"; do + log_warn " - $resource" + done + + log_info "Regenerating Android assets..." + + # Create assets directory if it doesn't exist + mkdir -p assets + + # Copy source assets to assets directory for capacitor-assets + cp resources/icon.png assets/ 2>/dev/null || log_warn "Could not copy icon.png" + cp resources/splash.png assets/ 2>/dev/null || log_warn "Could not copy splash.png" + cp resources/splash_dark.png assets/ 2>/dev/null || log_warn "Could not copy splash_dark.png" + + # Generate assets + if npx @capacitor/assets generate >/dev/null 2>&1; then + log_success "Android assets regenerated successfully" + + # Clean up temporary assets + rm -f assets/icon.png assets/splash.png assets/splash_dark.png + + # Verify the resources were created + local verification_failed=false + + if [ ! -f "android/app/src/main/res/drawable/splash.png" ]; then + log_error "Failed to generate drawable/splash.png" + verification_failed=true + fi + + for dir in "${mipmap_dirs[@]}"; do + if [ ! -f "android/app/src/main/res/$dir/ic_launcher.png" ]; then + log_error "Failed to generate $dir/ic_launcher.png" + verification_failed=true + fi + if [ ! -f "android/app/src/main/res/$dir/ic_launcher_round.png" ]; then + log_error "Failed to generate $dir/ic_launcher_round.png" + verification_failed=true + fi + done + + if [ "$verification_failed" = true ]; then + log_error "Asset generation completed but some resources are still missing." + log_info "You may need to manually create the missing resources or check the asset generation process." + return 1 + fi + else + log_error "Failed to generate Android assets" + log_info "You may need to manually create the missing resources:" + for resource in "${missing_drawables[@]}" "${missing_mipmaps[@]}"; do + log_info " - android/app/src/main/res/$resource" + done + return 1 + fi + else + log_success "All Android assets and resources validated successfully" + fi + + return 0 +} + # Default values BUILD_MODE="development" BUILD_TYPE="debug" @@ -101,7 +237,7 @@ parse_android_args() { --sync) SYNC_ONLY=true ;; - --assets) + --assets|--assets-only) ASSETS_ONLY=true ;; --deploy) @@ -179,12 +315,23 @@ parse_android_args "$@" # Print build header print_header "TimeSafari Android Build Process" + +# Validate dependencies before proceeding +validate_dependencies + +# Validate Android assets and resources +validate_android_assets || { + log_error "Android asset validation failed. Please fix the issues above and try again." + exit 9 +} + +# Log build start log_info "Starting Android build process at $(date)" log_info "Build mode: $BUILD_MODE" log_info "Build type: $BUILD_TYPE" # Setup environment for Capacitor build -setup_build_env "capacitor" +setup_build_env "capacitor" "$BUILD_MODE" # Override API servers for Android development if [ "$BUILD_MODE" = "development" ]; then @@ -254,9 +401,10 @@ if [ "$DEPLOY_APP" = true ]; then exit 0 fi -# Step 1: Check and fix Android resources -safe_execute "Checking Android resources" "$(dirname "$0")/check-android-resources.sh" || { - log_warning "Resource check found issues, but continuing with build..." +# Step 1: Validate asset configuration +safe_execute "Validating asset configuration" "npm run assets:validate" || { + log_warn "Asset validation found issues, but continuing with build..." + log_info "If you encounter build failures, please run 'npm install' first to ensure all dependencies are available." } # Step 2: Clean Android app @@ -337,6 +485,9 @@ if [ "$OPEN_STUDIO" = true ]; then log_info "Android Studio: opened" fi +# Reminder about dependency management +log_info "💡 Tip: If you encounter dependency issues, run 'npm install' to ensure all packages are up to date." + print_footer "Android Build" # Exit with success diff --git a/scripts/build-arch-guard.sh b/scripts/build-arch-guard.sh new file mode 100755 index 00000000..f61fa8d1 --- /dev/null +++ b/scripts/build-arch-guard.sh @@ -0,0 +1,355 @@ +#!/usr/bin/env bash +# +# Build Architecture Guard Script +# +# Author: Matthew Raymer +# Date: 2025-08-22 +# Purpose: Protects build-critical files by requiring BUILDING.md updates +# Enhanced to protect Android build system including asset validation, +# API routing, and resource generation logic +# +# Usage: +# ./scripts/build-arch-guard.sh --staged # Check staged files (pre-commit) +# ./scripts/build-arch-guard.sh --range # Check range (pre-push) +# ./scripts/build-arch-guard.sh # Check working directory +# + +set -euo pipefail + +# Sensitive paths that require BUILDING.md updates when modified +SENSITIVE=( + "vite.config.*" + "scripts/**" + "electron/**" + "android/**" + "ios/**" + "sw_scripts/**" + "sw_combine.js" + "Dockerfile" + "docker/**" + "capacitor.config.ts" + "capacitor-assets.config.json" # Critical for Android assets + "package.json" + "package-lock.json" + "yarn.lock" + "pnpm-lock.yaml" + "resources/**" # Source assets for Android +) + +# Documentation files that must be updated alongside sensitive changes +DOCS_REQUIRED=( + "BUILDING.md" + "doc/README-BUILD-GUARD.md" # Guard documentation +) + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +log_info() { + echo -e "${BLUE}[guard]${NC} $1" +} + +log_warn() { + echo -e "${YELLOW}[guard]${NC} $1" +} + +log_error() { + echo -e "${RED}[guard]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[guard]${NC} $1" +} + +# Collect files based on mode +collect_files() { + if [[ "${1:-}" == "--staged" ]]; then + # Pre-commit: check staged files + git diff --name-only --cached + elif [[ "${1:-}" == "--range" ]]; then + # Pre-push: check commits being pushed + RANGE="${2:-HEAD~1..HEAD}" + git diff --name-only "$RANGE" + else + # Default: check working directory changes + git diff --name-only HEAD + fi +} + +# Check if a file matches any sensitive pattern +matches_sensitive() { + local f="$1" + for pat in "${SENSITIVE[@]}"; do + # Convert glob pattern to regex + local rx="^${pat//\./\.}$" + rx="${rx//\*\*/.*}" + rx="${rx//\*/[^/]*}" + + if [[ "$f" =~ $rx ]]; then + return 0 + fi + done + return 1 +} + +# Check if documentation was updated +check_docs_updated() { + local changed_files=("$@") + + for changed_file in "${changed_files[@]}"; do + for required_doc in "${DOCS_REQUIRED[@]}"; do + if [[ "$changed_file" == "$required_doc" ]]; then + return 0 + fi + done + done + return 1 +} + +# Check if Android build system was modified +check_android_build_changes() { + local changed_files=("$@") + + for file in "${changed_files[@]}"; do + if [[ "$file" =~ ^android/ ]] || [[ "$file" =~ ^scripts/build-android\.sh$ ]]; then + return 0 + fi + done + return 1 +} + +# Check if asset configuration was modified +check_asset_config_changes() { + local changed_files=("$@") + + for file in "${changed_files[@]}"; do + if [[ "$file" =~ ^capacitor-assets\.config\.json$ ]] || [[ "$file" =~ ^resources/ ]]; then + return 0 + fi + done + return 1 +} + +# Enhanced validation for Android changes +validate_android_changes() { + local changed_files=("$@") + + if check_android_build_changes "${changed_files[@]}"; then + log_warn "Android build system changes detected!" + echo + echo "Android build system changes require enhanced validation:" + echo " - Test asset generation: npm run build:android --assets" + echo " - Test API routing modes: --dev and --dev --api-ip <custom>" + echo " - Verify resource fallback mechanisms" + echo " - Test across development/test/production modes" + echo + echo "Please ensure BUILDING.md includes Android-specific testing procedures." + echo + fi + + if check_asset_config_changes "${changed_files[@]}"; then + log_warn "Asset configuration changes detected!" + echo + echo "Asset configuration changes require validation:" + echo " - Test asset generation across all platforms" + echo " - Verify resource files are properly created" + echo " - Test asset validation scripts" + echo + fi +} + +# Feedback collection for continuous improvement +collect_feedback_data() { + local mode="$1" + local sensitive_touched=("${@:2}") + local timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + # Create feedback log entry + local feedback_log=".guard-feedback.log" + echo "[$timestamp] Guard execution: $mode" >> "$feedback_log" + echo " Sensitive files: ${sensitive_touched[*]}" >> "$feedback_log" + + # Log Android-specific changes for analysis + if check_android_build_changes "${sensitive_touched[@]}"; then + echo " Android changes detected" >> "$feedback_log" + fi + + # Log asset configuration changes for analysis + if check_asset_config_changes "${sensitive_touched[@]}"; then + echo " Asset config changes detected" >> "$feedback_log" + fi + + echo "" >> "$feedback_log" +} + +# Enhanced error handling with Android-specific guidance +handle_documentation_error() { + local sensitive_touched=("$@") + + log_error "Build-sensitive files changed but BUILDING.md was not updated!" + echo + echo "The following build-sensitive files were modified:" + for file in "${sensitive_touched[@]}"; do + echo " - $file" + done + echo + echo "When modifying build-critical files, you must also update BUILDING.md" + echo "to document any changes to the build process." + echo + + # Add Android-specific guidance + if check_android_build_changes "${sensitive_touched[@]}"; then + echo "⚠️ ANDROID BUILD SYSTEM CHANGES DETECTED ⚠️" + echo "Android changes require enhanced documentation including:" + echo " - Asset validation procedures" + echo " - API routing configuration" + echo " - Resource generation testing" + echo " - Platform-specific build modes" + echo + fi + + if check_asset_config_changes "${sensitive_touched[@]}"; then + echo "🎨 ASSET CONFIGURATION CHANGES DETECTED 🎨" + echo "Asset changes require documentation including:" + echo " - Asset generation procedures" + echo " - Resource validation steps" + echo " - Platform-specific asset requirements" + echo + fi + + echo "Please:" + echo " 1. Update BUILDING.md with relevant changes" + echo " 2. Stage the BUILDING.md changes: git add BUILDING.md" + echo " 3. Retry your commit/push" + echo + echo "💡 Feedback: If this guard is too strict or missing patterns," + echo " please report to the development team for continuous improvement." + echo + echo "📊 Feedback Categories:" + echo " - False positives (files flagged that shouldn't be)" + echo " - False negatives (sensitive files not caught)" + echo " - Missing patterns (new file types to protect)" + echo " - Overly strict (patterns too restrictive)" + echo " - Documentation gaps (missing guidance)" + echo " - Testing improvements (better procedures)" + echo + echo "📝 Report feedback to: Development team with specific examples" + echo +} + +# Main guard logic +main() { + local mode="${1:-}" + local arg="${2:-}" + + log_info "Running Build Architecture Guard..." + + # Collect changed files + changed_files=() + while IFS= read -r line; do + [[ -n "$line" ]] && changed_files+=("$line") + done < <(collect_files "$mode" "$arg") + + if [[ ${#changed_files[@]} -eq 0 ]]; then + log_info "No files changed, guard check passed" + exit 0 + fi + + log_info "Checking ${#changed_files[@]} changed files..." + + # Find sensitive files that were touched + sensitive_touched=() + for file in "${changed_files[@]}"; do + if matches_sensitive "$file"; then + sensitive_touched+=("$file") + fi + done + + # If no sensitive files were touched, allow the change + if [[ ${#sensitive_touched[@]} -eq 0 ]]; then + log_success "No build-sensitive files changed, guard check passed" + exit 0 + fi + + # Sensitive files were touched, log them + log_warn "Build-sensitive paths changed:" + for file in "${sensitive_touched[@]}"; do + echo " - $file" + done + + # Enhanced validation for Android changes + validate_android_changes "${changed_files[@]}" + + # Collect feedback data for continuous improvement + collect_feedback_data "$mode" "${sensitive_touched[@]}" + + # Check if required documentation was updated + if check_docs_updated "${changed_files[@]}"; then + log_success "BUILDING.md updated alongside build changes, guard check passed" + exit 0 + else + # Enhanced error handling with Android-specific guidance + handle_documentation_error "${sensitive_touched[@]}" + exit 2 + fi +} + +# Handle help flag +if [[ "${1:-}" =~ ^(-h|--help)$ ]]; then + echo "Build Architecture Guard Script" + echo + echo "Usage:" + echo " $0 [--staged|--range [RANGE]]" + echo + echo "Options:" + echo " --staged Check staged files (for pre-commit hook)" + echo " --range [RANGE] Check git range (for pre-push hook)" + echo " Default range: HEAD~1..HEAD" + echo " (no args) Check working directory changes" + echo " --feedback Show feedback analysis (for maintainers)" + echo + echo "Examples:" + echo " $0 --staged # Pre-commit check" + echo " $0 --range origin/main..HEAD # Pre-push check" + echo " $0 # Working directory check" + echo " $0 --feedback # Analyze guard effectiveness" + exit 0 +fi + +# Handle feedback analysis +if [[ "${1:-}" == "--feedback" ]]; then + if [[ -f ".guard-feedback.log" ]]; then + echo "Build Architecture Guard Feedback Analysis" + echo "==========================================" + echo + echo "Recent guard executions:" + echo + tail -20 ".guard-feedback.log" | while IFS= read -r line; do + if [[ "$line" =~ ^\[ ]]; then + echo "📅 $line" + elif [[ "$line" =~ ^\s*Sensitive\ files: ]]; then + echo "🔍 $line" + elif [[ "$line" =~ ^\s*Android\ changes ]]; then + echo "🤖 $line" + elif [[ "$line" =~ ^\s*Asset\ config ]]; then + echo "🎨 $line" + elif [[ "$line" =~ ^\s*$ ]]; then + echo "" + else + echo " $line" + fi + done + echo + echo "💡 Use this data to improve guard patterns and documentation" + echo "📊 Total executions: $(grep -c "Guard execution" .guard-feedback.log 2>/dev/null || echo "0")" + else + echo "No feedback data available yet. Run the guard to collect data." + fi + exit 0 +fi + +main "$@" diff --git a/scripts/build-electron.sh b/scripts/build-electron.sh index a7b1b1e8..96213afa 100755 --- a/scripts/build-electron.sh +++ b/scripts/build-electron.sh @@ -181,7 +181,7 @@ sync_capacitor() { copy_web_assets() { log_info "Copying web assets to Electron" safe_execute "Copying assets" "cp -r dist/* electron/app/" - safe_execute "Copying config" "cp capacitor.config.json electron/capacitor.config.json" + # Note: Electron has its own capacitor.config.ts file, so we don't copy the main config } # Compile TypeScript @@ -339,7 +339,7 @@ main_electron_build() { fi # Setup environment - setup_build_env "electron" + setup_build_env "electron" "$BUILD_MODE" setup_app_directories load_env_file ".env" diff --git a/scripts/build-ios.sh b/scripts/build-ios.sh index 9c3b2118..511358d5 100755 --- a/scripts/build-ios.sh +++ b/scripts/build-ios.sh @@ -173,20 +173,20 @@ check_ios_resources() { # Check for required assets if [ ! -f "assets/icon.png" ]; then - log_warning "App icon not found at assets/icon.png" + log_warn "App icon not found at assets/icon.png" fi if [ ! -f "assets/splash.png" ]; then - log_warning "Splash screen not found at assets/splash.png" + log_warn "Splash screen not found at assets/splash.png" fi # Check for iOS-specific files if [ ! -f "ios/App/App/Info.plist" ]; then - log_warning "Info.plist not found" + log_warn "Info.plist not found" fi if [ ! -f "ios/App/App/AppDelegate.swift" ]; then - log_warning "AppDelegate.swift not found" + log_warn "AppDelegate.swift not found" fi log_success "iOS resource check completed" @@ -311,7 +311,7 @@ log_info "Build mode: $BUILD_MODE" log_info "Build type: $BUILD_TYPE" # Setup environment for Capacitor build -setup_build_env "capacitor" +setup_build_env "capacitor" "$BUILD_MODE" # Override API servers for iOS development when custom IP is specified if [ "$BUILD_MODE" = "development" ] && [ -n "$CUSTOM_API_IP" ]; then diff --git a/scripts/build-web.sh b/scripts/build-web.sh index de0710a2..54c9c6a4 100755 --- a/scripts/build-web.sh +++ b/scripts/build-web.sh @@ -332,7 +332,7 @@ log_info "Serve build: $SERVE_BUILD" validate_web_environment # Setup environment for web build -setup_build_env "web" +setup_build_env "web" "$BUILD_MODE" # Setup application directories setup_app_directories diff --git a/scripts/check-android-resources.sh b/scripts/check-android-resources.sh deleted file mode 100755 index d328c051..00000000 --- a/scripts/check-android-resources.sh +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/bash - -# TimeSafari Android Resource Check Script -# Checks for missing Android resources and automatically fixes common issues -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -ANDROID_RES_DIR="$PROJECT_ROOT/android/app/src/main/res" -ASSETS_DIR="$PROJECT_ROOT/assets" - -echo "=== TimeSafari Android Resource Check ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Checking Android resources" - -# Function to check if a file exists -check_file() { - local file="$1" - local description="$2" - if [ -f "$file" ]; then - echo "[✓] $description: $file" - return 0 - else - echo "[✗] $description: $file (MISSING)" - return 1 - fi -} - -# Function to check if a directory exists and has files -check_directory() { - local dir="$1" - local description="$2" - if [ -d "$dir" ] && [ "$(ls -A "$dir" 2>/dev/null)" ]; then - echo "[✓] $description: $dir" - return 0 - else - echo "[✗] $description: $dir (MISSING OR EMPTY)" - return 1 - fi -} - -# Track issues -issues_found=0 -fixes_applied=0 - -echo "[INFO] Checking splash screen resources..." -# Check splash screen resources -if ! check_file "$ANDROID_RES_DIR/drawable/splash.png" "Splash screen (light)"; then - if [ -f "$ASSETS_DIR/splash.png" ]; then - echo "[FIX] Copying splash.png to Android resources..." - cp "$ASSETS_DIR/splash.png" "$ANDROID_RES_DIR/drawable/splash.png" - fixes_applied=$((fixes_applied + 1)) - else - issues_found=$((issues_found + 1)) - fi -fi - -if ! check_file "$ANDROID_RES_DIR/drawable/splash_dark.png" "Splash screen (dark)"; then - if [ -f "$ASSETS_DIR/splash_dark.png" ]; then - echo "[FIX] Copying splash_dark.png to Android resources..." - cp "$ASSETS_DIR/splash_dark.png" "$ANDROID_RES_DIR/drawable/splash_dark.png" - fixes_applied=$((fixes_applied + 1)) - else - issues_found=$((issues_found + 1)) - fi -fi - -echo "[INFO] Checking launcher icon resources..." -# Check launcher icon resources -required_icons=( - "mipmap-mdpi/ic_launcher.png" - "mipmap-hdpi/ic_launcher.png" - "mipmap-xhdpi/ic_launcher.png" - "mipmap-xxhdpi/ic_launcher.png" - "mipmap-xxxhdpi/ic_launcher.png" - "mipmap-anydpi-v26/ic_launcher.xml" - "mipmap-anydpi-v26/ic_launcher_round.xml" -) - -missing_icons=0 -for icon in "${required_icons[@]}"; do - if ! check_file "$ANDROID_RES_DIR/$icon" "Launcher icon: $icon"; then - missing_icons=$((missing_icons + 1)) - fi -done - -if [ $missing_icons -gt 0 ]; then - echo "[FIX] Missing launcher icons detected. Running icon generation script..." - if [ -f "$SCRIPT_DIR/generate-android-icons.sh" ]; then - "$SCRIPT_DIR/generate-android-icons.sh" - fixes_applied=$((fixes_applied + 1)) - else - echo "[ERROR] Icon generation script not found: $SCRIPT_DIR/generate-android-icons.sh" - issues_found=$((issues_found + 1)) - fi -fi - -echo "[INFO] Checking Capacitor platform status..." -# Check if Android platform is properly initialized -if [ ! -d "$PROJECT_ROOT/android" ]; then - echo "[ERROR] Android platform directory not found" - issues_found=$((issues_found + 1)) -elif [ ! -f "$PROJECT_ROOT/android/app/src/main/AndroidManifest.xml" ]; then - echo "[ERROR] AndroidManifest.xml not found - platform may be corrupted" - issues_found=$((issues_found + 1)) -else - echo "[✓] Android platform appears to be properly initialized" -fi - -# Check for common build issues -echo "[INFO] Checking for common build issues..." - -# Check for invalid resource names (dashes in filenames) -invalid_resources=$(find "$ANDROID_RES_DIR" -name "*-*" -type f 2>/dev/null | grep -E '\.(png|jpg|jpeg|gif|xml)$' || true) -if [ -n "$invalid_resources" ]; then - echo "[WARNING] Found resources with invalid names (containing dashes):" - echo "$invalid_resources" | while read -r file; do - echo " - $file" - done - echo "[INFO] Android resource names must contain only lowercase a-z, 0-9, or underscore" - issues_found=$((issues_found + 1)) -fi - -# Summary -echo "" -echo "=== Resource Check Summary ===" -if [ $issues_found -eq 0 ] && [ $fixes_applied -eq 0 ]; then - echo "[SUCCESS] All Android resources are present and valid" - exit 0 -elif [ $fixes_applied -gt 0 ]; then - echo "[SUCCESS] Fixed $fixes_applied resource issues automatically" - if [ $issues_found -gt 0 ]; then - echo "[WARNING] $issues_found issues remain that require manual attention" - exit 1 - else - exit 0 - fi -else - echo "[ERROR] Found $issues_found resource issues that require manual attention" - exit 1 -fi \ No newline at end of file diff --git a/scripts/check-dependencies.sh b/scripts/check-dependencies.sh new file mode 100755 index 00000000..c8e14e8b --- /dev/null +++ b/scripts/check-dependencies.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# check-dependencies.sh +# Author: Matthew Raymer +# Date: 2025-08-19 +# Description: Dependency validation script for TimeSafari development environment +# This script checks for critical dependencies required for building the application. + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +print_header "TimeSafari Dependency Validation" + +log_info "Checking development environment dependencies..." + +# Check Node.js version +if command -v node &> /dev/null; then + NODE_VERSION=$(node --version) + log_info "Node.js version: $NODE_VERSION" + + # Extract major version number + MAJOR_VERSION=$(echo $NODE_VERSION | sed 's/v\([0-9]*\)\..*/\1/') + if [ "$MAJOR_VERSION" -lt 18 ]; then + log_error "Node.js version $NODE_VERSION is too old. Please upgrade to Node.js 18 or later." + exit 1 + fi +else + log_error "Node.js is not installed. Please install Node.js 18 or later." + exit 1 +fi + +# Check npm version +if command -v npm &> /dev/null; then + NPM_VERSION=$(npm --version) + log_info "npm version: $NPM_VERSION" +else + log_error "npm is not installed. Please install npm." + exit 1 +fi + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + log_error "node_modules directory not found." + log_info "Please run: npm install" + exit 1 +fi + +# Check critical dependencies +log_info "Validating critical packages..." + +CRITICAL_DEPS=("tsx" "capacitor-assets" "vite") + +for dep in "${CRITICAL_DEPS[@]}"; do + if [ -f "node_modules/.bin/$dep" ]; then + log_success "✓ $dep found" + else + log_error "✗ $dep not found in node_modules/.bin" + log_info "This usually means the package wasn't installed properly." + log_info "Try running: npm install" + exit 1 + fi +done + +# Check TypeScript via npx +if npx tsc --version &> /dev/null; then + TSC_VERSION=$(npx tsc --version) + log_success "✓ TypeScript found: $TSC_VERSION" +else + log_error "✗ TypeScript not accessible via npx" + log_info "Try running: npm install" + exit 1 +fi + +# Check Capacitor CLI +if command -v npx &> /dev/null; then + if npx cap --version &> /dev/null; then + CAP_VERSION=$(npx cap --version) + log_success "✓ Capacitor CLI version: $CAP_VERSION" + else + log_error "✗ Capacitor CLI not accessible via npx" + log_info "Try running: npm install @capacitor/cli" + exit 1 + fi +else + log_error "npx is not available. Please ensure npm is properly installed." + exit 1 +fi + +# Check Android development tools +if command -v adb &> /dev/null; then + log_success "✓ Android Debug Bridge (adb) found" +else + log_warn "⚠ Android Debug Bridge (adb) not found" + log_info "This is only needed for Android development and testing." +fi + +if command -v gradle &> /dev/null; then + GRADLE_VERSION=$(gradle --version | head -n 1) + log_success "✓ Gradle found: $GRADLE_VERSION" +else + log_warn "⚠ Gradle not found in PATH" + log_info "This is only needed if building outside of Android Studio." +fi + +log_success "Dependency validation completed successfully!" +log_info "Your development environment is ready for TimeSafari development." + +print_footer "Dependency Validation" diff --git a/scripts/clean-android.sh b/scripts/clean-android.sh new file mode 100755 index 00000000..4fa354af --- /dev/null +++ b/scripts/clean-android.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# clean-android.sh +# Author: Matthew Raymer +# Date: 2025-08-19 +# Description: Clean Android app with timeout protection to prevent hanging +# This script safely uninstalls the TimeSafari app from connected Android devices +# with a 30-second timeout to prevent indefinite hanging. + +# Exit on any error +set -e + +# Source common utilities +source "$(dirname "$0")/common.sh" + +# Function to implement timeout for systems without timeout command +timeout_command() { + local timeout_seconds="$1" + shift + + # Check if timeout command exists + if command -v timeout &> /dev/null; then + timeout "$timeout_seconds" "$@" + else + # Fallback for systems without timeout (like macOS) + # Use perl to implement timeout + perl -e ' + eval { + local $SIG{ALRM} = sub { die "timeout" }; + alarm shift; + system @ARGV; + alarm 0; + }; + if ($@) { exit 1; } + ' "$timeout_seconds" "$@" + fi +} + +log_info "Starting Android cleanup process..." + +# Check if adb is available +if ! command -v adb &> /dev/null; then + log_error "adb command not found. Please install Android SDK Platform Tools." + exit 1 +fi + +# Check for connected devices +log_info "Checking for connected Android devices..." +if adb devices | grep -q 'device$'; then + log_info "Android device(s) found. Attempting to uninstall app..." + + # Try to uninstall with timeout + if timeout_command 30 adb uninstall app.timesafari.app; then + log_success "Successfully uninstalled TimeSafari app" + else + log_warn "Uninstall failed or timed out after 30 seconds" + log_info "This is normal if the app wasn't installed or device is unresponsive" + fi +else + log_info "No Android devices connected. Skipping uninstall." +fi + +log_success "Android cleanup process completed" diff --git a/scripts/common.sh b/scripts/common.sh index eb5956b6..ac1e3858 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -174,9 +174,9 @@ validate_env_vars() { # Function to set environment variables for different build types setup_build_env() { local build_type="$1" - local production="${2:-false}" + local build_mode="${2:-development}" - log_info "Setting up environment for $build_type build" + log_info "Setting up environment for $build_type build (mode: $build_mode)" # Get git hash for versioning local git_hash=$(get_git_hash) @@ -204,19 +204,19 @@ setup_build_env() { esac # Set API server environment variables based on build mode - if [ "$BUILD_MODE" = "development" ]; then + if [ "$build_mode" = "development" ]; then # For Capacitor development, use localhost by default # Android builds will override this in build-android.sh export VITE_DEFAULT_ENDORSER_API_SERVER="http://localhost:3000" export VITE_DEFAULT_PARTNER_API_SERVER="http://localhost:3000" log_debug "Development mode: Using localhost for Endorser and Partner APIs" export VITE_DEFAULT_IMAGE_API_SERVER="https://image-api.timesafari.app" - elif [ "$BUILD_MODE" = "test" ]; then + elif [ "$build_mode" = "test" ]; then export VITE_DEFAULT_ENDORSER_API_SERVER="https://test-api.endorser.ch" export VITE_DEFAULT_PARTNER_API_SERVER="https://test-partner-api.endorser.ch" log_debug "Test mode: Using test Endorser and Partner APIs" export VITE_DEFAULT_IMAGE_API_SERVER="https://image-api.timesafari.app" - elif [ "$BUILD_MODE" = "production" ]; then + elif [ "$build_mode" = "production" ]; then export VITE_DEFAULT_ENDORSER_API_SERVER="https://api.endorser.ch" export VITE_DEFAULT_PARTNER_API_SERVER="https://partner-api.endorser.ch" log_debug "Production mode: Using production API servers" diff --git a/scripts/fix-markdown.sh b/scripts/fix-markdown.sh new file mode 100755 index 00000000..b2a21f6d --- /dev/null +++ b/scripts/fix-markdown.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "🔧 Auto-fixing markdown formatting..." + +# Check if markdownlint is available +if ! command -v npx &> /dev/null; then + echo "❌ npx not found. Please install Node.js and npm first." + exit 1 +fi + +# Run markdownlint with auto-fix on project markdown files (exclude node_modules) +echo "📝 Fixing project markdown files..." +npx markdownlint "*.md" "*.mdc" "scripts/**/*.md" "src/**/*.md" "test-playwright/**/*.md" "resources/**/*.md" --config .markdownlint.json --fix 2>/dev/null || { + echo "⚠️ Some issues could not be auto-fixed. Check manually." +} + +echo "✅ Markdown auto-fix complete!" +echo "💡 Run 'npm run markdown:check' to verify all issues are resolved." diff --git a/scripts/generate-android-icons.sh b/scripts/generate-android-icons.sh deleted file mode 100755 index 432f2428..00000000 --- a/scripts/generate-android-icons.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash - -# TimeSafari Android Icon Generation Script -# Generates all required Android launcher icon sizes from assets/icon.png -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" -ASSETS_DIR="$PROJECT_ROOT/assets" -ANDROID_RES_DIR="$PROJECT_ROOT/android/app/src/main/res" - -echo "=== TimeSafari Android Icon Generation ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Starting Android icon generation" - -# Check if source icon exists -if [ ! -f "$ASSETS_DIR/icon.png" ]; then - echo "[ERROR] Source icon not found: $ASSETS_DIR/icon.png" - exit 1 -fi - -# Check if ImageMagick is available -if ! command -v convert &> /dev/null; then - echo "[ERROR] ImageMagick (convert) not found. Please install ImageMagick." - echo " Arch: sudo pacman -S imagemagick" - echo " Ubuntu: sudo apt-get install imagemagick" - echo " macOS: brew install imagemagick" - exit 1 -fi - -# Create mipmap directories if they don't exist -mkdir -p "$ANDROID_RES_DIR/mipmap-hdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-mdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xhdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xxhdpi" -mkdir -p "$ANDROID_RES_DIR/mipmap-xxxhdpi" - -echo "[INFO] Generating launcher icons..." - -# Generate launcher icons for different densities -# Android launcher icon sizes: mdpi=48, hdpi=72, xhdpi=96, xxhdpi=144, xxxhdpi=192 -convert "$ASSETS_DIR/icon.png" -resize 48x48 "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher.png" -convert "$ASSETS_DIR/icon.png" -resize 72x72 "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher.png" -convert "$ASSETS_DIR/icon.png" -resize 96x96 "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher.png" -convert "$ASSETS_DIR/icon.png" -resize 144x144 "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher.png" -convert "$ASSETS_DIR/icon.png" -resize 192x192 "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher.png" - -# Generate round launcher icons -convert "$ASSETS_DIR/icon.png" -resize 48x48 "$ANDROID_RES_DIR/mipmap-mdpi/ic_launcher_round.png" -convert "$ASSETS_DIR/icon.png" -resize 72x72 "$ANDROID_RES_DIR/mipmap-hdpi/ic_launcher_round.png" -convert "$ASSETS_DIR/icon.png" -resize 96x96 "$ANDROID_RES_DIR/mipmap-xhdpi/ic_launcher_round.png" -convert "$ASSETS_DIR/icon.png" -resize 144x144 "$ANDROID_RES_DIR/mipmap-xxhdpi/ic_launcher_round.png" -convert "$ASSETS_DIR/icon.png" -resize 192x192 "$ANDROID_RES_DIR/mipmap-xxxhdpi/ic_launcher_round.png" - -# Create background and foreground mipmap files -# These reference the existing drawable files -cat > "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher_background.xml" << 'EOF' -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background"/> -</adaptive-icon> -EOF - -cat > "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher_foreground.xml" << 'EOF' -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <foreground android:drawable="@drawable/ic_launcher_foreground"/> -</adaptive-icon> -EOF - -# Update the existing launcher XML files to reference the correct resources -cat > "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher.xml" << 'EOF' -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background"/> - <foreground android:drawable="@drawable/ic_launcher_foreground"/> -</adaptive-icon> -EOF - -cat > "$ANDROID_RES_DIR/mipmap-anydpi-v26/ic_launcher_round.xml" << 'EOF' -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background"/> - <foreground android:drawable="@drawable/ic_launcher_foreground"/> -</adaptive-icon> -EOF - -echo "[SUCCESS] Generated Android launcher icons:" -echo " - mipmap-mdpi/ic_launcher.png (48x48)" -echo " - mipmap-hdpi/ic_launcher.png (72x72)" -echo " - mipmap-xhdpi/ic_launcher.png (96x96)" -echo " - mipmap-xxhdpi/ic_launcher.png (144x144)" -echo " - mipmap-xxxhdpi/ic_launcher.png (192x192)" -echo " - Updated mipmap-anydpi-v26 XML files" -echo "[SUCCESS] Android icon generation completed successfully!" \ No newline at end of file diff --git a/scripts/generate-icons.sh b/scripts/generate-icons.sh deleted file mode 100755 index bed6fd14..00000000 --- a/scripts/generate-icons.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# Create directories if they don't exist -mkdir -p android/app/src/main/res/mipmap-mdpi -mkdir -p android/app/src/main/res/mipmap-hdpi -mkdir -p android/app/src/main/res/mipmap-xhdpi -mkdir -p android/app/src/main/res/mipmap-xxhdpi -mkdir -p android/app/src/main/res/mipmap-xxxhdpi - -# Generate placeholder icons using ImageMagick -convert -size 48x48 xc:blue -gravity center -pointsize 20 -fill white -annotate 0 "TS" android/app/src/main/res/mipmap-mdpi/ic_launcher.png -convert -size 72x72 xc:blue -gravity center -pointsize 30 -fill white -annotate 0 "TS" android/app/src/main/res/mipmap-hdpi/ic_launcher.png -convert -size 96x96 xc:blue -gravity center -pointsize 40 -fill white -annotate 0 "TS" android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -convert -size 144x144 xc:blue -gravity center -pointsize 60 -fill white -annotate 0 "TS" android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -convert -size 192x192 xc:blue -gravity center -pointsize 80 -fill white -annotate 0 "TS" android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png - -# Copy to round versions -cp android/app/src/main/res/mipmap-mdpi/ic_launcher.png android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -cp android/app/src/main/res/mipmap-hdpi/ic_launcher.png android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -cp android/app/src/main/res/mipmap-xhdpi/ic_launcher.png android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -cp android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -cp android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png \ No newline at end of file diff --git a/scripts/git-hooks/README.md b/scripts/git-hooks/README.md new file mode 100644 index 00000000..4f6cb126 --- /dev/null +++ b/scripts/git-hooks/README.md @@ -0,0 +1,124 @@ +# TimeSafari Git Hooks + +This directory contains custom Git hooks for the TimeSafari project. + +## Debug Code Checker Hook + +### Overview + +The `pre-commit` hook automatically checks for debug code when committing to protected branches (master, main, production, release). This prevents debug statements from accidentally reaching production code. + +### How It Works + +1. **Branch Detection**: Only runs on protected branches (configurable) +2. **File Filtering**: Automatically skips test files, scripts, and documentation +3. **Pattern Matching**: Detects common debug patterns using regex +4. **Commit Prevention**: Blocks commits containing debug code + +### Protected Branches (Default) + +- `master` +- `main` +- `production` +- `release` +- `stable` + +### Debug Patterns Detected + +- **Console statements**: `console.log`, `console.debug`, `console.error` +- **Template debug**: `Debug:`, `debug:` in Vue templates +- **Debug constants**: `DEBUG_`, `debug_` variables +- **HTML debug**: `<!-- debug` comments +- **Debug attributes**: `debug="true"` attributes +- **Vue debug**: `v-if="debug"`, `v-show="debug"` +- **Debug TODOs**: `TODO debug`, `FIXME debug` + +### Files Automatically Skipped + +- Test files: `*.test.js`, `*.spec.ts`, `*.test.vue` +- Scripts: `scripts/` directory +- Test directories: `test-*` directories +- Documentation: `docs/`, `*.md`, `*.txt` +- Config files: `*.json`, `*.yml`, `*.yaml` +- IDE files: `.cursor/` directory + +### Configuration + +Edit `.git/hooks/debug-checker.config` to customize: + +- Protected branches +- Debug patterns +- Skip patterns +- Logging level + +### Testing the Hook + +Run the test script to verify the hook works: + +```bash +./scripts/test-debug-hook.sh +``` + +### Manual Testing + +1. Make changes to a file with debug code +2. Stage the file: `git add <filename>` +3. Try to commit: `git commit -m 'test'` +4. Hook should prevent commit if debug code is found + +### Bypassing the Hook (Emergency) + +If you absolutely need to commit debug code to a protected branch: + +```bash +git commit --no-verify -m "emergency: debug code needed" +``` + +⚠️ **Warning**: This bypasses all pre-commit hooks. Use sparingly and only in emergencies. + +### Troubleshooting + +#### Hook not running + +- Ensure the hook is executable: `chmod +x .git/hooks/pre-commit` +- Check if you're on a protected branch +- Verify the hook file exists and has correct permissions + +#### False positives + +- Add legitimate debug patterns to skip patterns in config +- Use proper logging levels (`logger.info`, `logger.debug`) instead of console +- Move debug code to feature branches first + +#### Hook too strict + +- Modify debug patterns in config file +- Add more file types to skip patterns +- Adjust protected branch list + +### Best Practices + +1. **Use feature branches** for development with debug code +2. **Use proper logging** instead of console statements +3. **Test thoroughly** before merging to protected branches +4. **Review commits** to ensure no debug code slips through +5. **Keep config updated** as project needs change + +### Integration with CI/CD + +This hook works locally. For CI/CD pipelines, consider: + +- Running the same checks in your build process +- Adding ESLint rules for console statements +- Using TypeScript strict mode +- Adding debug code detection to PR checks + +### Support + +If you encounter issues: + +1. Check the hook output for specific error messages +2. Verify your branch is in the protected list +3. Review the configuration file +4. Test with the provided test script +5. Check file permissions and git setup diff --git a/scripts/git-hooks/debug-checker.config b/scripts/git-hooks/debug-checker.config new file mode 100644 index 00000000..1301ea87 --- /dev/null +++ b/scripts/git-hooks/debug-checker.config @@ -0,0 +1,86 @@ +# TimeSafari Debug Checker Configuration +# Edit this file to customize protected branches and debug patterns + +# Protected branches where debug code checking is enforced +# Add or remove branches as needed +PROTECTED_BRANCHES=( + "master" + "main" + "production" + "release" + "stable" +) + +# Debug patterns to detect (regex patterns) +# Add or remove patterns as needed +DEBUG_PATTERNS=( + # Console statements + "console\." + + # Template debug text + "Debug:" + "debug:" + + # Debug constants and variables + "DEBUG_" + "debug_" + + # HTML debug comments + "<!-- debug" + + # Debug attributes + "debug.*=" + + # Vue debug patterns + "v-if.*debug" + "v-show.*debug" + + # Common debug text + "TODO.*debug" + "FIXME.*debug" + + # Debug imports (uncomment if you want to catch these) + # "import.*debug" + # "require.*debug" +) + +# Files and directories to skip during checking +# Add patterns to exclude from debug checking +SKIP_PATTERNS=( + "\.(test|spec)\.(js|ts|vue)$" # Test files (must have .test. or .spec.) + "^scripts/" # Scripts directory + "^test-.*/" # Test directories (must end with /) + "^\.git/" # Git directory + "^node_modules/" # Dependencies + "^docs/" # Documentation + "^\.cursor/" # Cursor IDE files + "\.md$" # Markdown files + "\.txt$" # Text files + "\.json$" # JSON config files + "\.yml$" # YAML config files + "\.yaml$" # YAML config files +) + +# Files that are whitelisted for console statements +# These files may contain intentional console.log statements that are +# properly whitelisted with eslint-disable-next-line no-console comments +WHITELIST_FILES=( + "src/services/platforms/WebPlatformService.ts" # Worker context logging + "src/services/platforms/CapacitorPlatformService.ts" # Platform-specific logging + "src/services/platforms/ElectronPlatformService.ts" # Electron-specific logging + "src/services/QRScanner/.*" # QR Scanner services + "src/utils/logger.ts" # Logger utility itself + "src/utils/LogCollector.ts" # Log collection utilities + "scripts/.*" # Build and utility scripts + "test-.*/.*" # Test directories + ".*\.test\..*" # Test files + ".*\.spec\..*" # Spec files +) + +# Logging level (debug, info, warn, error) +LOG_LEVEL="info" + +# Exit codes +EXIT_SUCCESS=0 +EXIT_DEBUG_FOUND=1 +EXIT_ERROR=2 diff --git a/scripts/git-hooks/pre-commit b/scripts/git-hooks/pre-commit new file mode 100755 index 00000000..f783c953 --- /dev/null +++ b/scripts/git-hooks/pre-commit @@ -0,0 +1,252 @@ +#!/bin/bash + +# TimeSafari Pre-commit Hook - Debug Code Checker +# Only runs on master or specified branches to catch debug code before it reaches production + +# Hook directory +HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CONFIG_FILE="$HOOK_DIR/debug-checker.config" + +# Default configuration (fallback if config file is missing) +DEFAULT_PROTECTED_BRANCHES=("master" "main" "production" "release") +DEFAULT_DEBUG_PATTERNS=( + "console\." + "Debug:" + "debug:" + "DEBUG_" + "debug_" + "<!-- debug" + "debug.*=" +) +DEFAULT_WHITELIST_FILES=( + "src/services/platforms/WebPlatformService.ts" + "src/services/platforms/CapacitorPlatformService.ts" + "src/services/platforms/ElectronPlatformService.ts" +) + +# Load configuration from file if it exists +load_config() { + if [[ -f "$CONFIG_FILE" ]]; then + # Source the config file to load variables + # We'll use a safer approach by reading and parsing + PROTECTED_BRANCHES=() + DEBUG_PATTERNS=() + SKIP_PATTERNS=() + WHITELIST_FILES=() + + # Read protected branches + while IFS= read -r line; do + if [[ "$line" =~ ^PROTECTED_BRANCHES=\( ]]; then + # Start reading array + while IFS= read -r line; do + if [[ "$line" =~ ^\)$ ]]; then + break + fi + if [[ "$line" =~ \"([^\"]+)\" ]]; then + PROTECTED_BRANCHES+=("${BASH_REMATCH[1]}") + fi + done + fi + done < "$CONFIG_FILE" + + # Read debug patterns + while IFS= read -r line; do + if [[ "$line" =~ ^DEBUG_PATTERNS=\( ]]; then + while IFS= read -r line; do + if [[ "$line" =~ ^\)$ ]]; then + break + fi + if [[ "$line" =~ \"([^\"]+)\" ]]; then + DEBUG_PATTERNS+=("${BASH_REMATCH[1]}") + fi + done + fi + done < "$CONFIG_FILE" + + # Read skip patterns + while IFS= read -r line; do + if [[ "$line" =~ ^SKIP_PATTERNS=\( ]]; then + while IFS= read -r line; do + if [[ "$line" =~ ^\)$ ]]; then + break + fi + if [[ "$line" =~ \"([^\"]+)\" ]]; then + SKIP_PATTERNS+=("${BASH_REMATCH[1]}") + fi + done + fi + done < "$CONFIG_FILE" + + # Read whitelist files + while IFS= read -r line; do + if [[ "$line" =~ ^WHITELIST_FILES=\( ]]; then + while IFS= read -r line; do + if [[ "$line" =~ ^\)$ ]]; then + break + fi + if [[ "$line" =~ \"([^\"]+)\" ]]; then + WHITELIST_FILES+=("${BASH_REMATCH[1]}") + fi + done + fi + done < "$CONFIG_FILE" + fi + + # Use defaults if config loading failed + if [[ ${#PROTECTED_BRANCHES[@]} -eq 0 ]]; then + PROTECTED_BRANCHES=("${DEFAULT_PROTECTED_BRANCHES[@]}") + fi + + if [[ ${#DEBUG_PATTERNS[@]} -eq 0 ]]; then + DEBUG_PATTERNS=("${DEFAULT_DEBUG_PATTERNS[@]}") + fi + + if [[ ${#SKIP_PATTERNS[@]} -eq 0 ]]; then + SKIP_PATTERNS=("${DEFAULT_SKIP_PATTERNS[@]}") + fi + + if [[ ${#WHITELIST_FILES[@]} -eq 0 ]]; then + WHITELIST_FILES=("${DEFAULT_WHITELIST_FILES[@]}") + fi + +} + +# Check if current branch is protected +is_protected_branch() { + local branch="$1" + for protected in "${PROTECTED_BRANCHES[@]}"; do + if [[ "$branch" == "$protected" ]]; then + return 0 + fi + done + return 1 +} + +# Check if file should be skipped +should_skip_file() { + local file="$1" + for pattern in "${SKIP_PATTERNS[@]}"; do + if [[ "$file" =~ $pattern ]]; then + return 0 + fi + done + return 1 +} + +# Check if file is whitelisted for console statements +is_whitelisted_file() { + local file="$1" + for whitelisted in "${WHITELIST_FILES[@]}"; do + if [[ "$file" =~ $whitelisted ]]; then + return 0 + fi + done + return 1 +} + +# Main execution +main() { + # Load configuration + load_config + + # Get current branch name + CURRENT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null) + + if [[ -z "$CURRENT_BRANCH" ]]; then + echo "⚠️ Could not determine current branch, skipping debug check" + exit 0 + fi + + # Check if we should run the hook + if ! is_protected_branch "$CURRENT_BRANCH"; then + echo "🔒 Pre-commit hook skipped - not on protected branch ($CURRENT_BRANCH)" + echo " Protected branches: ${PROTECTED_BRANCHES[*]}" + exit 0 + fi + + echo "🔍 Running debug code check on protected branch: $CURRENT_BRANCH" + echo " Using config: $CONFIG_FILE" + + # Get all staged files (modified, added, copied, merged) + ALL_STAGED_FILES=$(git diff --cached --name-only) + + + + if [ -z "$ALL_STAGED_FILES" ]; then + echo "✅ No staged files to check" + exit 0 + fi + + # Initialize error tracking + ERRORS_FOUND=0 + ERROR_MESSAGES=() + FILES_CHECKED=0 + + # Check each staged file for debug patterns + for file in $ALL_STAGED_FILES; do + # Skip files that should be ignored + if should_skip_file "$file"; then + continue + fi + + FILES_CHECKED=$((FILES_CHECKED + 1)) + + # Check for debug patterns in the file + for pattern in "${DEBUG_PATTERNS[@]}"; do + # Skip console pattern checks for whitelisted files + if [[ "$pattern" == "console\." ]] && is_whitelisted_file "$file"; then + continue + fi + + # For new files, check the file content directly + # For modified files, check the staged diff + if [[ -f "$file" ]]; then + # New file - check content directly + if grep -E "$pattern" "$file" > /dev/null; then + ERRORS_FOUND=$((ERRORS_FOUND + 1)) + ERROR_MESSAGES+=("🚨 $file: Found debug pattern '$pattern'") + fi + else + # Modified file - check staged diff + if git diff --cached "$file" | grep -E "$pattern" > /dev/null; then + ERRORS_FOUND=$((ERRORS_FOUND + 1)) + ERROR_MESSAGES+=("🚨 $file: Found debug pattern '$pattern'") + fi + fi + done + done + + # Report results + if [ $ERRORS_FOUND -gt 0 ]; then + echo "" + echo "❌ Debug code detected in staged files!" + echo " Branch: $CURRENT_BRANCH" + echo " Files checked: $FILES_CHECKED" + echo " Errors found: $ERRORS_FOUND" + echo "" + for msg in "${ERROR_MESSAGES[@]}"; do + echo " $msg" + done + echo "" + echo "💡 Please remove debug code before committing to $CURRENT_BRANCH" + echo " Common debug patterns to check:" + echo " - console.log, console.debug, console.error" + echo " - Debug: or debug: in templates" + echo " - DEBUG_ constants" + echo " - HTML comments with debug" + echo "" + echo " If debug code is intentional, consider:" + echo " - Moving to a feature branch first" + echo " - Using proper logging levels (logger.info, logger.debug)" + echo " - Adding debug code to .gitignore or .debugignore" + echo "" + echo " Configuration file: $CONFIG_FILE" + exit 1 + else + echo "✅ No debug code found in $FILES_CHECKED staged files" + exit 0 + fi +} + +# Run main function +main "$@" diff --git a/scripts/install-debug-hook.sh b/scripts/install-debug-hook.sh new file mode 100755 index 00000000..c6a1cf85 --- /dev/null +++ b/scripts/install-debug-hook.sh @@ -0,0 +1,171 @@ +#!/bin/bash + +# TimeSafari Debug Hook Installer +# Run this script in any repository to install the debug pre-commit hook + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}🔧 TimeSafari Debug Hook Installer${NC}" +echo "=============================================" + +# Check if we're in a git repository +if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo -e "${RED}❌ Error: Not in a git repository${NC}" + echo "Please run this script from within a git repository" + exit 1 +fi + +# Get repository root +REPO_ROOT=$(git rev-parse --show-toplevel) +HOOKS_DIR="$REPO_ROOT/.git/hooks" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +echo -e "${BLUE}Repository:${NC} $REPO_ROOT" +echo -e "${BLUE}Hooks directory:${NC} $HOOKS_DIR" +echo -e "${BLUE}Script directory:${NC} $SCRIPT_DIR" + +# Check if hooks directory exists +if [[ ! -d "$HOOKS_DIR" ]]; then + echo -e "${RED}❌ Error: Hooks directory not found${NC}" + echo "This repository may not be properly initialized" + exit 1 +fi + +# Check if we have the hook files in the repository +HOOK_SCRIPT="$SCRIPT_DIR/git-hooks/pre-commit" +CONFIG_FILE="$SCRIPT_DIR/git-hooks/debug-checker.config" + +if [[ ! -f "$HOOK_SCRIPT" ]]; then + echo -e "${RED}❌ Error: Pre-commit hook script not found${NC}" + echo "Expected location: $HOOK_SCRIPT" + echo "Make sure you're running this from the TimeSafari repository" + exit 1 +fi + +if [[ ! -f "$CONFIG_FILE" ]]; then + echo -e "${RED}❌ Error: Debug checker config not found${NC}" + echo "Expected location: $CONFIG_FILE" + echo "Make sure you're running this from the TimeSafari repository" + exit 1 +fi + +# Check if already installed +if [[ -f "$HOOKS_DIR/pre-commit" && -f "$HOOKS_DIR/debug-checker.config" ]]; then + echo -e "${YELLOW}⚠️ Debug hook already appears to be installed${NC}" + echo -e " Checking if update is needed..." + + # Check if files are different + if diff "$HOOK_SCRIPT" "$HOOKS_DIR/pre-commit" > /dev/null 2>&1; then + echo -e " ${GREEN}✅${NC} Hook script is up to date" + HOOK_UP_TO_DATE=true + else + echo -e " ${YELLOW}⚠️ Hook script differs - will update${NC}" + HOOK_UP_TO_DATE=false + fi + + if diff "$CONFIG_FILE" "$HOOKS_DIR/debug-checker.config" > /dev/null 2>&1; then + echo -e " ${GREEN}✅${NC} Config file is up to date" + CONFIG_UP_TO_DATE=true + else + echo -e " ${YELLOW}⚠️ Config file differs - will update${NC}" + CONFIG_UP_TO_DATE=false + fi + + if [[ "$HOOK_UP_TO_DATE" == true && "$CONFIG_UP_TO_DATE" == true ]]; then + echo -e "\n${GREEN}✅ Debug hook is already up to date!${NC}" + echo -e " No installation needed" + else + echo -e "\n${BLUE}Updating existing installation...${NC}" + fi +else + echo -e "\n${BLUE}Installing debug hook...${NC}" +fi + +# Copy/update the hook script if needed +if [[ "$HOOK_UP_TO_DATE" != true ]]; then + cp "$HOOK_SCRIPT" "$HOOKS_DIR/pre-commit" + chmod +x "$HOOKS_DIR/pre-commit" + echo -e " ${GREEN}✅${NC} Pre-commit hook installed/updated" +fi + +# Copy/update the config file if needed +if [[ "$CONFIG_UP_TO_DATE" != true ]]; then + cp "$CONFIG_FILE" "$HOOKS_DIR/debug-checker.config" + echo -e " ${GREEN}✅${NC} Configuration file installed/updated" +fi + +# Copy/update the README if needed +README_FILE="$SCRIPT_DIR/git-hooks/README.md" +if [[ -f "$README_FILE" ]]; then + if [[ ! -f "$HOOKS_DIR/README.md" ]] || ! diff "$README_FILE" "$HOOKS_DIR/README.md" > /dev/null 2>&1; then + cp "$README_FILE" "$HOOKS_DIR/README.md" + echo -e " ${GREEN}✅${NC} Documentation installed/updated" + else + echo -e " ${GREEN}✅${NC} Documentation is up to date" + fi +fi + +echo -e "\n${GREEN}🎉 Debug hook installation complete!${NC}" + +# Test the installation +echo -e "\n${BLUE}Testing installation...${NC}" +if [[ -x "$HOOKS_DIR/pre-commit" ]]; then + echo -e " ${GREEN}✅${NC} Hook is executable" +else + echo -e " ${RED}❌${NC} Hook is not executable" +fi + +if [[ -f "$HOOKS_DIR/debug-checker.config" ]]; then + echo -e " ${GREEN}✅${NC} Config file exists" +else + echo -e " ${RED}❌${NC} Config file missing" +fi + +# Show current branch status +CURRENT_BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "detached") +echo -e "\n${BLUE}Current branch:${NC} $CURRENT_BRANCH" + +# Check if this is a protected branch +PROTECTED_BRANCHES=("master" "main" "production" "release" "stable") +IS_PROTECTED=false + +for branch in "${PROTECTED_BRANCHES[@]}"; do + if [[ "$CURRENT_BRANCH" == "$branch" ]]; then + IS_PROTECTED=true + break + fi +done + +if [[ "$IS_PROTECTED" == true ]]; then + echo -e "${YELLOW}⚠️ You're on a protected branch ($CURRENT_BRANCH)${NC}" + echo -e " The debug hook will now run on all commits to this branch" + echo -e " Consider switching to a feature branch for development" +else + echo -e "${GREEN}✅ You're on a feature branch ($CURRENT_BRANCH)${NC}" + echo -e " The debug hook will be skipped on this branch" + echo -e " You can develop with debug code freely" +fi + +echo -e "\n${BLUE}Next steps:${NC}" +echo "1. The hook will now run automatically on protected branches" +echo "2. Test it by trying to commit a file with debug code" +echo "3. Use feature branches for development with debug code" +echo "4. Check the README.md in .git/hooks/ for more information" + +echo -e "\n${BLUE}To test the hook:${NC}" +echo "1. Create a test file with debug code (e.g., console.log('test'))" +echo "2. Stage it: git add <filename>" +echo "3. Try to commit: git commit -m 'test'" +echo "4. The hook should prevent the commit if debug code is found" + +echo -e "\n${BLUE}To uninstall:${NC}" +echo "rm $HOOKS_DIR/pre-commit" +echo "rm $HOOKS_DIR/debug-checker.config" +echo "rm $HOOKS_DIR/README.md" diff --git a/scripts/purge-generated-assets.sh b/scripts/purge-generated-assets.sh deleted file mode 100755 index 3be21db6..00000000 --- a/scripts/purge-generated-assets.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - -# TimeSafari Generated Assets Purge Script -# Removes generated Android assets and resources from git history -# Author: Matthew Raymer - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -echo "=== TimeSafari Generated Assets Purge ===" -echo "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] Starting git history cleanup" - -# Check if we're in a git repository -if [ ! -d ".git" ]; then - echo "[ERROR] Not in a git repository. Please run this script from the project root." - exit 1 -fi - -# Check if we have uncommitted changes -if [ -n "$(git status --porcelain)" ]; then - echo "[ERROR] You have uncommitted changes. Please commit or stash them first." - echo "Current status:" - git status --short - exit 1 -fi - -# Create backup branch -BACKUP_BRANCH="backup-before-asset-purge-$(date +%Y%m%d-%H%M%S)" -echo "[INFO] Creating backup branch: $BACKUP_BRANCH" -git branch "$BACKUP_BRANCH" - -echo "[INFO] Starting git filter-branch to remove generated assets..." - -# Use git filter-branch to remove the directories from history -git filter-branch --force --index-filter ' - # Remove generated Android assets directory - git rm -rf --cached --ignore-unmatch android/app/src/main/assets/public/ 2>/dev/null || true - - # Remove generated Android resources (but keep config files) - git rm -rf --cached --ignore-unmatch android/app/src/main/res/drawable*/ 2>/dev/null || true - git rm -rf --cached --ignore-unmatch android/app/src/main/res/mipmap*/ 2>/dev/null || true - git rm -rf --cached --ignore-unmatch android/app/src/main/res/values/ic_launcher_background.xml 2>/dev/null || true - - # Keep configuration files - git add android/app/src/main/res/values/strings.xml 2>/dev/null || true - git add android/app/src/main/res/values/styles.xml 2>/dev/null || true - git add android/app/src/main/res/layout/activity_main.xml 2>/dev/null || true - git add android/app/src/main/res/xml/config.xml 2>/dev/null || true - git add android/app/src/main/res/xml/file_paths.xml 2>/dev/null || true -' --prune-empty --tag-name-filter cat -- --all - -echo "[INFO] Cleaning up git filter-branch temporary files..." -rm -rf .git/refs/original/ -git reflog expire --expire=now --all -git gc --prune=now --aggressive - -echo "[SUCCESS] Generated assets purged from git history!" -echo "[INFO] Backup branch created: $BACKUP_BRANCH" -echo "[INFO] Repository size should be significantly reduced" -echo "" -echo "Next steps:" -echo "1. Test that the repository works correctly" -echo "2. Force push to remote: git push --force-with-lease origin <branch>" -echo "3. Inform team members to re-clone or reset their local repositories" -echo "4. Delete backup branch when confident: git branch -D $BACKUP_BRANCH" \ No newline at end of file diff --git a/scripts/setup-markdown-hooks.sh b/scripts/setup-markdown-hooks.sh new file mode 100644 index 00000000..6c3e72ec --- /dev/null +++ b/scripts/setup-markdown-hooks.sh @@ -0,0 +1,214 @@ +#!/bin/bash + +# Setup Markdown Pre-commit Hooks +# This script installs pre-commit hooks that automatically fix markdown formatting + +set -e + +echo "🔧 Setting up Markdown Pre-commit Hooks..." + +# Check if pre-commit is installed +if ! command -v pre-commit &> /dev/null; then + echo "📦 Installing pre-commit..." + pip install pre-commit +else + echo "✅ pre-commit already installed" +fi + +# Create .pre-commit-config.yaml if it doesn't exist +if [ ! -f .pre-commit-config.yaml ]; then + echo "📝 Creating .pre-commit-config.yaml..." + cat > .pre-commit-config.yaml << 'EOF' +repos: + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.38.0 + hooks: + - id: markdownlint + args: [--fix, --config, .markdownlint.json] + files: \.(md|mdc)$ + description: "Auto-fix markdown formatting issues" + stages: [commit] + additional_dependencies: [markdownlint-cli] + + - repo: local + hooks: + - id: markdown-format-check + name: Markdown Format Validation + entry: bash -c 'echo "Checking markdown files..." && npx markdownlint --config .markdownlint.json "$@"' + language: system + files: \.(md|mdc)$ + stages: [commit] + description: "Validate markdown formatting" + pass_filenames: true + + - repo: local + hooks: + - id: markdown-line-length + name: Markdown Line Length Check + entry: bash -c ' + for file in "$@"; do + if [[ "$file" =~ \.(md|mdc)$ ]]; then + echo "Checking line length in $file..." + if grep -q ".\{81,\}" "$file"; then + echo "❌ Line length violations found in $file" + echo "Lines exceeding 80 characters:" + grep -n ".\{81,\}" "$file" | head -5 + exit 1 + fi + fi + done + ' + language: system + files: \.(md|mdc)$ + stages: [commit] + description: "Check markdown line length (80 chars max)" + pass_filenames: true + + - repo: local + hooks: + - id: markdown-blank-lines + name: Markdown Blank Line Validation + entry: bash -c ' + for file in "$@"; do + if [[ "$file" =~ \.(md|mdc)$ ]]; then + echo "Checking blank lines in $file..." + # Check for multiple consecutive blank lines + if grep -q "^$" "$file" && grep -A1 "^$" "$file" | grep -q "^$"; then + echo "❌ Multiple consecutive blank lines found in $file" + exit 1 + fi + # Check for missing blank lines around headings + if grep -B1 "^##" "$file" | grep -v "^##" | grep -v "^$" | grep -v "^--"; then + echo "❌ Missing blank line before heading in $file" + exit 1 + fi + fi + done + ' + language: system + files: \.(md|mdc)$ + stages: [commit] + description: "Validate markdown blank line formatting" + pass_filenames: true +EOF + echo "✅ Created .pre-commit-config.yaml" +else + echo "✅ .pre-commit-config.yaml already exists" +fi + +# Install the pre-commit hooks +echo "🔗 Installing pre-commit hooks..." +pre-commit install + +# Install markdownlint if not present +if ! command -v npx &> /dev/null; then + echo "📦 Installing Node.js dependencies..." + npm install --save-dev markdownlint-cli +else + if ! npx markdownlint --version &> /dev/null; then + echo "📦 Installing markdownlint-cli..." + npm install --save-dev markdownlint-cli + else + echo "✅ markdownlint-cli already available" + fi +fi + +# Create a markdown auto-fix script +echo "📝 Creating markdown auto-fix script..." +cat > scripts/fix-markdown.sh << 'EOF' +#!/bin/bash + +# Auto-fix markdown formatting issues +# Usage: ./scripts/fix-markdown.sh [file_or_directory] + +set -e + +FIX_MARKDOWN() { + local target="$1" + + if [ -f "$target" ]; then + # Fix single file + if [[ "$target" =~ \.(md|mdc)$ ]]; then + echo "🔧 Fixing markdown formatting in $target..." + npx markdownlint --fix "$target" || true + fi + elif [ -d "$target" ]; then + # Fix all markdown files in directory + echo "🔧 Fixing markdown formatting in $target..." + find "$target" -name "*.md" -o -name "*.mdc" | while read -r file; do + echo " Processing $file..." + npx markdownlint --fix "$file" || true + done + else + echo "❌ Target $target not found" + exit 1 + fi +} + +# Default to current directory if no target specified +TARGET="${1:-.}" +FIX_MARKDOWN "$TARGET" + +echo "✅ Markdown formatting fixes applied!" +echo "💡 Run 'git diff' to see what was changed" +EOF + +chmod +x scripts/fix-markdown.sh + +# Create a markdown validation script +echo "📝 Creating markdown validation script..." +cat > scripts/validate-markdown.sh << 'EOF' +#!/bin/bash + +# Validate markdown formatting without auto-fixing +# Usage: ./scripts/validate-markdown.sh [file_or_directory] + +set -e + +VALIDATE_MARKDOWN() { + local target="$1" + + if [ -f "$target" ]; then + # Validate single file + if [[ "$target" =~ \.(md|mdc)$ ]]; then + echo "🔍 Validating markdown formatting in $target..." + npx markdownlint "$target" + fi + elif [ -d "$target" ]; then + # Validate all markdown files in directory + echo "🔍 Validating markdown formatting in $target..." + find "$target" -name "*.md" -o -name "*.mdc" | while read -r file; do + echo " Checking $file..." + npx markdownlint "$file" || true + done + else + echo "❌ Target $target not found" + exit 1 + fi +} + +# Default to current directory if no target specified +TARGET="${1:-.}" +VALIDATE_MARKDOWN "$TARGET" + +echo "✅ Markdown validation complete!" +EOF + +chmod +x scripts/validate-markdown.sh + +echo "" +echo "🎉 Markdown Pre-commit Hooks Setup Complete!" +echo "" +echo "📋 What was installed:" +echo " ✅ pre-commit hooks for automatic markdown formatting" +echo " ✅ .pre-commit-config.yaml with markdown rules" +echo " ✅ scripts/fix-markdown.sh for manual fixes" +echo " ✅ scripts/validate-markdown.sh for validation" +echo "" +echo "🚀 Usage:" +echo " • Hooks run automatically on commit" +echo " • Manual fix: ./scripts/fix-markdown.sh [file/dir]" +echo " • Manual check: ./scripts/validate-markdown.sh [file/dir]" +echo " • Test hooks: pre-commit run --all-files" +echo "" +echo "💡 The hooks will now automatically fix markdown issues before commits!" diff --git a/scripts/test-debug-hook.sh b/scripts/test-debug-hook.sh new file mode 100755 index 00000000..20b6ca91 --- /dev/null +++ b/scripts/test-debug-hook.sh @@ -0,0 +1,117 @@ +#!/bin/bash + +# Test script for the debug pre-commit hook +# This script helps verify that the hook is working correctly + +set -e + +echo "🧪 Testing TimeSafari Debug Pre-commit Hook" +echo "=============================================" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Test directory +TEST_DIR="$(mktemp -d)" +echo -e "${BLUE}Created test directory: $TEST_DIR${NC}" + +# Function to cleanup +cleanup() { + echo -e "${YELLOW}Cleaning up test directory...${NC}" + rm -rf "$TEST_DIR" +} + +# Set trap to cleanup on exit +trap cleanup EXIT + +# Function to run test +run_test() { + local test_name="$1" + local test_file="$2" + local expected_exit="$3" + + echo -e "\n${BLUE}Running test: $test_name${NC}" + + # Create test file + echo "$test_file" > "$TEST_DIR/test.vue" + + # Stage the file + cd "$TEST_DIR" + git init > /dev/null 2>&1 + git add test.vue > /dev/null 2>&1 + + # Run the hook + if bash ../../.git/hooks/pre-commit > hook_output.txt 2>&1; then + exit_code=0 + else + exit_code=$? + fi + + # Check result + if [[ $exit_code -eq $expected_exit ]]; then + echo -e " ${GREEN}✅ PASS${NC} - Exit code: $exit_code (expected: $expected_exit)" + else + echo -e " ${RED}❌ FAIL${NC} - Exit code: $exit_code (expected: $expected_exit)" + echo -e " ${YELLOW}Hook output:${NC}" + cat hook_output.txt + fi + + # Cleanup git + rm -rf .git + rm -f hook_output.txt +} + +# Test cases +echo -e "\n${BLUE}Test Case 1: Clean file (should pass)${NC}" +run_test "Clean file" "// No debug code here" 0 + +echo -e "\n${BLUE}Test Case 2: Console statement (should fail)${NC}" +run_test "Console statement" "console.log('debug info')" 1 + +echo -e "\n${BLUE}Test Case 3: Debug template (should fail)${NC}" +run_test "Debug template" "Debug: {{ isMapReady ? 'Map Ready' : 'Map Loading' }}" 1 + +echo -e "\n${BLUE}Test Case 4: Debug constant (should fail)${NC}" +run_test "Debug constant" "const DEBUG_MODE = true" 1 + +echo -e "\n${BLUE}Test Case 5: Mixed content (should fail)${NC}" +run_test "Mixed content" "// Some normal code\nconsole.debug('test')\n// More normal code" 1 + +echo -e "\n${BLUE}Test Case 6: HTML debug comment (should fail)${NC}" +run_test "HTML debug comment" "<!-- debug: this is debug info -->" 1 + +echo -e "\n${BLUE}Test Case 7: Debug attribute (should fail)${NC}" +run_test "Debug attribute" "<div debug='true'>content</div>" 1 + +echo -e "\n${BLUE}Test Case 8: Test file (should be skipped)${NC}" +run_test "Test file" "console.log('this should be skipped')" 0 + +# Test branch detection +echo -e "\n${BLUE}Testing branch detection...${NC}" +cd "$TEST_DIR" +git init > /dev/null 2>&1 +git checkout -b feature-branch > /dev/null 2>&1 +echo "console.log('debug')" > test.vue +git add test.vue > /dev/null 2>&1 + +if bash ../../.git/hooks/pre-commit > hook_output.txt 2>&1; then + echo -e " ${GREEN}✅ PASS${NC} - Hook skipped on feature branch" +else + echo -e " ${RED}❌ FAIL${NC} - Hook should have been skipped on feature branch" + echo -e " ${YELLOW}Hook output:${NC}" + cat hook_output.txt +fi + +rm -rf .git +rm -f hook_output.txt + +echo -e "\n${GREEN}🎉 All tests completed!${NC}" +echo -e "\n${BLUE}To test manually:${NC}" +echo "1. Make changes to a file with debug code" +echo "2. Stage the file: git add <filename>" +echo "3. Try to commit: git commit -m 'test'" +echo "4. The hook should prevent the commit if debug code is found" diff --git a/scripts/test-env.sh b/scripts/test-env.sh index 2baad4e6..2c5409d8 100755 --- a/scripts/test-env.sh +++ b/scripts/test-env.sh @@ -17,34 +17,40 @@ parse_args "$@" print_header "Environment Variable Test" log_info "Testing environment variable handling at $(date)" -# Test 1: Capacitor environment -log_info "Test 1: Setting up Capacitor environment..." -setup_build_env "capacitor" +# Test 1: Capacitor environment (development) +log_info "Test 1: Setting up Capacitor environment (development mode)..." +setup_build_env "capacitor" "development" print_env_vars "VITE_" echo "" -# Test 2: Web environment -log_info "Test 2: Setting up Web environment..." -setup_build_env "web" +# Test 2: Web environment (development) +log_info "Test 2: Setting up Web environment (development mode)..." +setup_build_env "web" "development" print_env_vars "VITE_" echo "" -# Test 3: Production Capacitor environment -log_info "Test 3: Setting up Production Capacitor environment..." -setup_build_env "capacitor" "true" +# Test 3: Capacitor test environment +log_info "Test 3: Setting up Capacitor environment (test mode)..." +setup_build_env "capacitor" "test" print_env_vars "VITE_" echo "" -# Test 4: Application directories -log_info "Test 4: Setting up application directories..." +# Test 4: Capacitor production environment +log_info "Test 4: Setting up Capacitor environment (production mode)..." +setup_build_env "capacitor" "production" +print_env_vars "VITE_" +echo "" + +# Test 5: Application directories +log_info "Test 5: Setting up application directories..." setup_app_directories -# Test 5: Load .env file (if it exists) -log_info "Test 5: Loading .env file..." +# Test 6: Load .env file (if it exists) +log_info "Test 6: Loading .env file..." load_env_file ".env" -# Test 6: Git hash -log_info "Test 6: Getting git hash..." +# Test 7: Git hash +log_info "Test 7: Getting git hash..." GIT_HASH=$(get_git_hash) log_info "Git hash: $GIT_HASH" diff --git a/scripts/type-safety-check.sh b/scripts/type-safety-check.sh new file mode 100755 index 00000000..399343df --- /dev/null +++ b/scripts/type-safety-check.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# Type Safety Pre-commit Check Script +# This script ensures type safety before commits by running linting and type checking + +set -e + +echo "🔍 Running Type Safety Pre-commit Checks..." + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${GREEN}✅ $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}⚠️ $1${NC}" +} + +print_error() { + echo -e "${RED}❌ $1${NC}" +} + +# Check if we're in the right directory +if [ ! -f "package.json" ]; then + print_error "Must run from project root directory" + exit 1 +fi + +# Step 1: Run ESLint with TypeScript rules +print_status "Running ESLint TypeScript checks..." +if npm run lint > /dev/null 2>&1; then + print_status "ESLint passed - no type safety issues found" +else + print_error "ESLint failed - type safety issues detected" + echo "" + echo "Running lint with details..." + npm run lint + echo "" + print_error "Please fix the above type safety issues before committing" + exit 1 +fi + +# Step 2: Run TypeScript type checking +print_status "Running TypeScript type checking..." +if npm run type-check > /dev/null 2>&1; then + print_status "TypeScript compilation passed" +else + print_error "TypeScript compilation failed" + echo "" + echo "Running type check with details..." + npm run type-check + echo "" + print_error "Please fix the above TypeScript errors before committing" + exit 1 +fi + +# Step 3: Check for any remaining 'any' types +print_status "Scanning for any remaining 'any' types..." +ANY_COUNT=$(grep -r "any" src/ --include="*.ts" --include="*.vue" | grep -v "// eslint-disable" | grep -v "eslint-disable-next-line" | wc -l) + +if [ "$ANY_COUNT" -eq 0 ]; then + print_status "No 'any' types found in source code" +else + print_warning "Found $ANY_COUNT instances of 'any' type usage" + echo "" + echo "Instances found:" + grep -r "any" src/ --include="*.ts" --include="*.vue" | grep -v "// eslint-disable" | grep -v "eslint-disable-next-line" || true + echo "" + print_error "Please replace 'any' types with proper TypeScript types before committing" + exit 1 +fi + +# Step 4: Verify database migration status +print_status "Checking database migration status..." +if grep -r "databaseUtil" src/ --include="*.ts" --include="*.vue" > /dev/null 2>&1; then + print_warning "Found databaseUtil imports - ensure migration is complete" + echo "" + echo "Files with databaseUtil imports:" + grep -r "databaseUtil" src/ --include="*.ts" --include="*.vue" | head -5 || true + echo "" + print_warning "Consider completing database migration to PlatformServiceMixin" +else + print_status "No databaseUtil imports found - migration appears complete" +fi + +# All checks passed +echo "" +print_status "All type safety checks passed! 🎉" +print_status "Your code is ready for commit" +echo "" +echo "📚 Remember to follow the Type Safety Guidelines:" +echo " - docs/typescript-type-safety-guidelines.md" +echo " - Use proper error handling patterns" +echo " - Leverage existing type definitions" +echo " - Run 'npm run lint-fix' for automatic fixes" + +exit 0 diff --git a/scripts/validate-markdown.sh b/scripts/validate-markdown.sh new file mode 100755 index 00000000..f54f9dee --- /dev/null +++ b/scripts/validate-markdown.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -euo pipefail + +echo "🔍 Validating markdown formatting..." + +# Check if markdownlint is available +if ! command -v npx &> /dev/null; then + echo "❌ npx not found. Please install Node.js and npm first." + exit 1 +fi + +# Run markdownlint on project markdown files (exclude node_modules) +echo "📝 Checking project markdown files..." +npx markdownlint "*.md" "*.mdc" "scripts/**/*.md" "src/**/*.md" "test-playwright/**/*.md" "resources/**/*.md" --config .markdownlint.json 2>/dev/null || { + echo "❌ Markdown validation failed. Run 'npm run markdown:fix' to auto-fix issues." + exit 1 +} + +echo "✅ All markdown files pass validation!" diff --git a/src/App.vue b/src/App.vue index 0b190521..35457383 100644 --- a/src/App.vue +++ b/src/App.vue @@ -4,7 +4,7 @@ <!-- Messages in the upper-right - https://github.com/emmanuelsw/notiwind --> <NotificationGroup group="alert"> <div - class="fixed z-[90] top-[max(1rem,env(safe-area-inset-top))] right-4 left-4 sm:left-auto sm:w-full sm:max-w-sm flex flex-col items-start justify-end" + class="fixed z-[120] top-[max(1rem,env(safe-area-inset-top),var(--safe-area-inset-top,0px))] right-4 left-4 sm:left-auto sm:w-full sm:max-w-sm flex flex-col items-start justify-end" > <Notification v-slot="{ notifications, close }" @@ -28,8 +28,12 @@ class="w-full max-w-sm mx-auto mb-3 overflow-hidden bg-slate-900/90 text-white rounded-lg shadow-md" > <div class="w-full px-4 py-3 overflow-hidden"> - <h4 class="font-semibold text-ellipsis overflow-hidden">{{ notification.title }}</h4> - <p class="text-sm text-ellipsis overflow-hidden">{{ notification.text }}</p> + <h4 class="font-semibold text-ellipsis overflow-hidden"> + {{ notification.title }} + </h4> + <p class="text-sm text-ellipsis overflow-hidden"> + {{ notification.text }} + </p> </div> </div> @@ -46,9 +50,15 @@ ></font-awesome> </div> - <div class="relative w-full pl-4 pr-8 py-2 text-slate-900 overflow-hidden"> - <h4 class="font-semibold text-ellipsis overflow-hidden">{{ notification.title }}</h4> - <p class="text-sm text-ellipsis overflow-hidden">{{ notification.text }}</p> + <div + class="relative w-full pl-4 pr-8 py-2 text-slate-900 overflow-hidden" + > + <h4 class="font-semibold text-ellipsis overflow-hidden"> + {{ notification.title }} + </h4> + <p class="text-sm text-ellipsis overflow-hidden"> + {{ notification.text }} + </p> <button class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-slate-200 text-slate-600" @@ -72,9 +82,15 @@ ></font-awesome> </div> - <div class="relative w-full pl-4 pr-8 py-2 text-emerald-900 overflow-hidden"> - <h4 class="font-semibold text-ellipsis overflow-hidden">{{ notification.title }}</h4> - <p class="text-sm text-ellipsis overflow-hidden">{{ notification.text }}</p> + <div + class="relative w-full pl-4 pr-8 py-2 text-emerald-900 overflow-hidden" + > + <h4 class="font-semibold text-ellipsis overflow-hidden"> + {{ notification.title }} + </h4> + <p class="text-sm text-ellipsis overflow-hidden"> + {{ notification.text }} + </p> <button class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-emerald-200 text-emerald-600" @@ -98,9 +114,15 @@ ></font-awesome> </div> - <div class="relative w-full pl-4 pr-8 py-2 text-amber-900 overflow-hidden"> - <h4 class="font-semibold text-ellipsis overflow-hidden">{{ notification.title }}</h4> - <p class="text-sm text-ellipsis overflow-hidden">{{ notification.text }}</p> + <div + class="relative w-full pl-4 pr-8 py-2 text-amber-900 overflow-hidden" + > + <h4 class="font-semibold text-ellipsis overflow-hidden"> + {{ notification.title }} + </h4> + <p class="text-sm text-ellipsis overflow-hidden"> + {{ notification.text }} + </p> <button class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-amber-200 text-amber-600" @@ -124,9 +146,15 @@ ></font-awesome> </div> - <div class="relative w-full pl-4 pr-8 py-2 text-rose-900 overflow-hidden"> - <h4 class="font-semibold text-ellipsis overflow-hidden">{{ notification.title }}</h4> - <p class="text-sm text-ellipsis overflow-hidden">{{ notification.text }}</p> + <div + class="relative w-full pl-4 pr-8 py-2 text-rose-900 overflow-hidden" + > + <h4 class="font-semibold text-ellipsis overflow-hidden"> + {{ notification.title }} + </h4> + <p class="text-sm text-ellipsis overflow-hidden"> + {{ notification.text }} + </p> <button class="absolute top-2 right-2 px-0.5 py-0 rounded-full bg-rose-200 text-rose-600" @@ -147,7 +175,9 @@ "-permission", "-mute", "-off" --> <NotificationGroup group="modal"> - <div class="fixed z-[100] top-[env(safe-area-inset-top)] inset-x-0 w-full"> + <div + class="fixed z-[100] top-[max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))] inset-x-0 w-full" + > <Notification v-slot="{ notifications, close }" enter="transform ease-out duration-300 transition" @@ -478,13 +508,32 @@ export default class App extends Vue { <style> #Content { - padding-left: max(1.5rem, env(safe-area-inset-left)); - padding-right: max(1.5rem, env(safe-area-inset-right)); - padding-top: max(1.5rem, env(safe-area-inset-top)); - padding-bottom: max(1.5rem, env(safe-area-inset-bottom)); + padding-left: max( + 1.5rem, + env(safe-area-inset-left), + var(--safe-area-inset-left, 0px) + ); + padding-right: max( + 1.5rem, + env(safe-area-inset-right), + var(--safe-area-inset-right, 0px) + ); + padding-top: max( + 1.5rem, + env(safe-area-inset-top), + var(--safe-area-inset-top, 0px) + ); + padding-bottom: max( + 1.5rem, + env(safe-area-inset-bottom), + var(--safe-area-inset-bottom, 0px) + ); } #QuickNav ~ #Content { - padding-bottom: calc(env(safe-area-inset-bottom) + 6.333rem); + padding-bottom: calc( + max(env(safe-area-inset-bottom), var(--safe-area-inset-bottom, 0px)) + + 6.333rem + ); } </style> diff --git a/src/assets/icons.json b/src/assets/icons.json deleted file mode 100644 index 434421e6..00000000 --- a/src/assets/icons.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "warning": { - "fillRule": "evenodd", - "d": "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z", - "clipRule": "evenodd" - }, - "spinner": { - "d": "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" - }, - "chart": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" - }, - "plus": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M12 4v16m8-8H4" - }, - "settings": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" - }, - "settingsDot": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M15 12a3 3 0 11-6 0 3 3 0 016 0z" - }, - "lock": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z" - }, - "download": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" - }, - "check": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" - }, - "edit": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" - }, - "trash": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" - }, - "plusCircle": { - "strokeLinecap": "round", - "strokeLinejoin": "round", - "strokeWidth": "2", - "d": "M12 6v6m0 0v6m0-6h6m-6 0H6" - }, - "info": { - "fillRule": "evenodd", - "d": "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z", - "clipRule": "evenodd" - } -} \ No newline at end of file diff --git a/src/assets/styles/tailwind.css b/src/assets/styles/tailwind.css index 60f6579d..f6457ff3 100644 --- a/src/assets/styles/tailwind.css +++ b/src/assets/styles/tailwind.css @@ -14,4 +14,12 @@ transform: translateX(100%); background-color: #FFF !important; } + + .dialog-overlay { + @apply z-[100] fixed inset-0 bg-black/50 flex justify-center items-center p-6; + } + + .dialog { + @apply bg-white p-4 rounded-lg w-full max-w-lg; + } } \ No newline at end of file diff --git a/src/components/ActivityListItem.vue b/src/components/ActivityListItem.vue index 257259b9..39dfcffa 100644 --- a/src/components/ActivityListItem.vue +++ b/src/components/ActivityListItem.vue @@ -252,7 +252,7 @@ import { GiveRecordWithContactInfo } from "@/interfaces/give"; import EntityIcon from "./EntityIcon.vue"; import { isHiddenDid } from "../libs/endorserServer"; import ProjectIcon from "./ProjectIcon.vue"; -import { createNotifyHelpers } from "@/utils/notify"; +import { createNotifyHelpers, NotifyFunction } from "@/utils/notify"; import { NOTIFY_PERSON_HIDDEN, NOTIFY_UNKNOWN_PERSON, @@ -273,7 +273,7 @@ export default class ActivityListItem extends Vue { isHiddenDid = isHiddenDid; notify!: ReturnType<typeof createNotifyHelpers>; - $notify!: (notification: any, timeout?: number) => void; + $notify!: NotifyFunction; created() { this.notify = createNotifyHelpers(this.$notify); @@ -288,8 +288,7 @@ export default class ActivityListItem extends Vue { } get fetchAmount(): string { - const claim = - (this.record.fullClaim as any)?.claim || this.record.fullClaim; + const claim = this.record.fullClaim; const amount = claim.object?.amountOfThisGood ? this.displayAmount(claim.object.unitCode, claim.object.amountOfThisGood) @@ -299,8 +298,7 @@ export default class ActivityListItem extends Vue { } get description(): string { - const claim = - (this.record.fullClaim as any)?.claim || this.record.fullClaim; + const claim = this.record.fullClaim; return `${claim?.description || ""}`; } diff --git a/src/components/ContactNameDialog.vue b/src/components/ContactNameDialog.vue index e0f715ea..3eb116cf 100644 --- a/src/components/ContactNameDialog.vue +++ b/src/components/ContactNameDialog.vue @@ -1,7 +1,7 @@ <!-- similar to UserNameDialog --> <template> - <div v-if="visible" :class="overlayClasses"> - <div :class="dialogClasses"> + <div v-if="visible" class="dialog-overlay"> + <div class="dialog"> <h1 :class="titleClasses">{{ title }}</h1> {{ message }} Note that their name is only stored on this device. @@ -61,20 +61,6 @@ export default class ContactNameDialog extends Vue { title = "Contact Name"; visible = false; - /** - * CSS classes for the modal overlay backdrop - */ - get overlayClasses(): string { - return "z-index-50 fixed top-0 left-0 right-0 bottom-0 bg-black/50 flex justify-center items-center p-6"; - } - - /** - * CSS classes for the modal dialog container - */ - get dialogClasses(): string { - return "bg-white p-4 rounded-lg w-full max-w-[500px]"; - } - /** * CSS classes for the dialog title */ diff --git a/src/components/DataExportSection.vue b/src/components/DataExportSection.vue index a21be7b2..61bf4753 100644 --- a/src/components/DataExportSection.vue +++ b/src/components/DataExportSection.vue @@ -171,6 +171,8 @@ export default class DataExportSection extends Vue { * @throws {Error} If export fails */ public async exportDatabase(): Promise<void> { + // Note that similar code is in ContactsView.vue exportContactData() + if (this.isExporting) { return; // Prevent multiple simultaneous exports } diff --git a/src/components/EntityGrid.vue b/src/components/EntityGrid.vue index ff995652..ec5fe236 100644 --- a/src/components/EntityGrid.vue +++ b/src/components/EntityGrid.vue @@ -22,7 +22,7 @@ projects, and special entities with selection. * * @author Matthew Raymer */ <!-- "Unnamed" entity --> <SpecialEntityCard entity-type="unnamed" - label="Unnamed" + :label="unnamedEntityName" icon="circle-question" :entity-data="unnamedEntityData" :notify="notify" @@ -83,6 +83,7 @@ import ShowAllCard from "./ShowAllCard.vue"; import { Contact } from "../db/tables/contacts"; import { PlanData } from "../interfaces/records"; import { NotificationIface } from "../constants/app"; +import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; /** * EntityGrid - Unified grid layout for displaying people or projects @@ -159,6 +160,10 @@ export default class EntityGrid extends Vue { @Prop({ default: "other party" }) conflictContext!: string; + /** Whether to hide the "Show All" navigation */ + @Prop({ default: false }) + hideShowAll!: boolean; + /** * Function to determine which entities to display (allows parent control) * @@ -245,7 +250,9 @@ export default class EntityGrid extends Vue { * Whether to show the "Show All" navigation */ get shouldShowAll(): boolean { - return this.entities.length > 0 && this.showAllRoute !== ""; + return ( + !this.hideShowAll && this.entities.length > 0 && this.showAllRoute !== "" + ); } /** @@ -271,10 +278,17 @@ export default class EntityGrid extends Vue { get unnamedEntityData(): { did: string; name: string } { return { did: "", - name: "Unnamed", + name: UNNAMED_ENTITY_NAME, }; } + /** + * Get the unnamed entity name constant + */ + get unnamedEntityName(): string { + return UNNAMED_ENTITY_NAME; + } + /** * Check if a person DID is conflicted */ @@ -304,16 +318,13 @@ export default class EntityGrid extends Vue { /** * Handle special entity selection from SpecialEntityCard + * Treat "You" and "Unnamed" as person entities */ - handleEntitySelected(event: { - type: string; - entityType: string; - data: { did?: string; name: string }; - }): void { + handleEntitySelected(event: { data: { did?: string; name: string } }): void { + // Convert special entities to person entities since they represent people this.emitEntitySelected({ - type: "special", - entityType: event.entityType, - data: event.data, + type: "person", + data: event.data as Contact, }); } @@ -321,13 +332,11 @@ export default class EntityGrid extends Vue { @Emit("entity-selected") emitEntitySelected(data: { - type: "person" | "project" | "special"; - entityType?: string; - data: Contact | PlanData | { did?: string; name: string }; + type: "person" | "project"; + data: Contact | PlanData; }): { - type: "person" | "project" | "special"; - entityType?: string; - data: Contact | PlanData | { did?: string; name: string }; + type: "person" | "project"; + data: Contact | PlanData; } { return data; } diff --git a/src/components/EntitySelectionStep.vue b/src/components/EntitySelectionStep.vue index 56426462..2fb6bcac 100644 --- a/src/components/EntitySelectionStep.vue +++ b/src/components/EntitySelectionStep.vue @@ -27,6 +27,7 @@ Matthew Raymer */ :show-all-query-params="showAllQueryParams" :notify="notify" :conflict-context="conflictContext" + :hide-show-all="hideShowAll" @entity-selected="handleEntitySelected" /> @@ -55,9 +56,8 @@ interface EntityData { * Entity selection event data structure */ interface EntitySelectionEvent { - type: "person" | "project" | "special"; - entityType?: string; - data: Contact | PlanData | EntityData; + type: "person" | "project"; + data: Contact | PlanData; } /** @@ -154,6 +154,10 @@ export default class EntitySelectionStep extends Vue { @Prop() notify?: (notification: NotificationIface, timeout?: number) => void; + /** Whether to hide the "Show All" navigation */ + @Prop({ default: false }) + hideShowAll!: boolean; + /** * CSS classes for the cancel button */ diff --git a/src/components/EntitySummaryButton.vue b/src/components/EntitySummaryButton.vue index 50f25078..80890eff 100644 --- a/src/components/EntitySummaryButton.vue +++ b/src/components/EntitySummaryButton.vue @@ -42,8 +42,8 @@ computed CSS properties * * @author Matthew Raymer */ <p class="text-xs text-slate-500 leading-1 -mb-1 uppercase"> {{ label }} </p> - <h3 class="font-semibold truncate"> - {{ entity?.name || "Unnamed" }} + <h3 :class="nameClasses"> + {{ displayName }} </h3> </div> @@ -62,6 +62,7 @@ import { Component, Prop, Vue } from "vue-facing-decorator"; import EntityIcon from "./EntityIcon.vue"; import ProjectIcon from "./ProjectIcon.vue"; import { Contact } from "../db/tables/contacts"; +import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; /** * Entity interface for both person and project entities @@ -138,6 +139,38 @@ export default class EntitySummaryButton extends Vue { return this.editable ? "text-blue-500" : "text-slate-400"; } + /** + * Computed CSS classes for the entity name + */ + get nameClasses(): string { + const baseClasses = "font-semibold truncate"; + + // Add italic styling for special "Unnamed" or entities without set names + if (!this.entity?.name || this.entity?.did === "") { + return `${baseClasses} italic text-slate-500`; + } + + return baseClasses; + } + + /** + * Computed display name for the entity + */ + get displayName(): string { + // If the entity has a set name, use that name + if (this.entity?.name) { + return this.entity.name; + } + + // If the entity is the special "Unnamed", use "Unnamed" + if (this.entity?.did === "") { + return UNNAMED_ENTITY_NAME; + } + + // If the entity does not have a set name, but is not the special "Unnamed", use their DID + return this.entity?.did; + } + /** * Handle click event - only call function prop if editable * Allows parent to control edit behavior and validation diff --git a/src/components/FeedFilters.vue b/src/components/FeedFilters.vue index e0ab2f9e..91a0db6b 100644 --- a/src/components/FeedFilters.vue +++ b/src/components/FeedFilters.vue @@ -101,6 +101,7 @@ import { import { Router } from "vue-router"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; +import { logger } from "@/utils/logger"; @Component({ components: { @@ -119,11 +120,13 @@ export default class FeedFilters extends Vue { isNearby = false; settingChanged = false; visible = false; + activeDid = ""; - async open(onCloseIfChanged: () => void) { + async open(onCloseIfChanged: () => void, activeDid: string) { this.onCloseIfChanged = onCloseIfChanged; + this.activeDid = activeDid; - const settings = await this.$settings(); + const settings = await this.$accountSettings(activeDid); this.hasVisibleDid = !!settings.filterFeedByVisible; this.isNearby = !!settings.filterFeedByNearby; if (settings.searchBoxes && settings.searchBoxes.length > 0) { @@ -137,6 +140,7 @@ export default class FeedFilters extends Vue { async toggleHasVisibleDid() { this.settingChanged = true; this.hasVisibleDid = !this.hasVisibleDid; + await this.$updateSettings({ filterFeedByVisible: this.hasVisibleDid, }); @@ -145,9 +149,18 @@ export default class FeedFilters extends Vue { async toggleNearby() { this.settingChanged = true; this.isNearby = !this.isNearby; + + logger.debug("[FeedFilters] 🔄 Toggling nearby filter:", { + newValue: this.isNearby, + settingChanged: this.settingChanged, + activeDid: this.activeDid, + }); + await this.$updateSettings({ filterFeedByNearby: this.isNearby, }); + + logger.debug("[FeedFilters] ✅ Nearby filter updated in settings"); } async clearAll() { @@ -179,43 +192,27 @@ export default class FeedFilters extends Vue { } close() { + logger.debug("[FeedFilters] 🚪 Closing dialog:", { + settingChanged: this.settingChanged, + hasCallback: !!this.onCloseIfChanged, + }); + if (this.settingChanged) { + logger.debug("[FeedFilters] 🔄 Settings changed, calling callback"); this.onCloseIfChanged(); } this.visible = false; } done() { + logger.debug("[FeedFilters] ✅ Done button clicked"); this.close(); } } </script> <style> -.dialog-overlay { - z-index: 50; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - #dialogFeedFilters.dialog-overlay { - z-index: 100; overflow: scroll; } - -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 500px; -} </style> diff --git a/src/components/GiftedDialog.vue b/src/components/GiftedDialog.vue index 7fcc1747..d91e68b3 100644 --- a/src/components/GiftedDialog.vue +++ b/src/components/GiftedDialog.vue @@ -29,6 +29,7 @@ :unit-code="unitCode" :offer-id="offerId" :notify="$notify" + :hide-show-all="hideShowAll" @entity-selected="handleEntitySelected" @cancel="cancel" /> @@ -80,13 +81,14 @@ import EntitySelectionStep from "../components/EntitySelectionStep.vue"; import GiftDetailsStep from "../components/GiftDetailsStep.vue"; import { PlanData } from "../interfaces/records"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; -import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; +import { createNotifyHelpers, TIMEOUTS, NotifyFunction } from "@/utils/notify"; import { NOTIFY_GIFT_ERROR_NEGATIVE_AMOUNT, NOTIFY_GIFT_ERROR_NO_DESCRIPTION, NOTIFY_GIFTED_DETAILS_NO_IDENTIFIER, NOTIFY_GIFTED_DETAILS_RECORDING_GIVE, } from "@/constants/notifications"; +import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; @Component({ components: { @@ -98,7 +100,7 @@ import { mixins: [PlatformServiceMixin], }) export default class GiftedDialog extends Vue { - $notify!: (notification: any, timeout?: number) => void; + $notify!: NotifyFunction; notify!: ReturnType<typeof createNotifyHelpers>; /** @@ -114,6 +116,7 @@ export default class GiftedDialog extends Vue { @Prop() fromProjectId = ""; @Prop() toProjectId = ""; @Prop() isFromProjectView = false; + @Prop() hideShowAll = false; @Prop({ default: "person" }) giverEntityType = "person" as | "person" | "project"; @@ -224,15 +227,6 @@ export default class GiftedDialog extends Vue { this.allMyDids = await retrieveAccountDids(); - if (this.giver && !this.giver.name) { - this.giver.name = didInfo( - this.giver.did, - this.activeDid, - this.allMyDids, - this.allContacts, - ); - } - if ( this.giverEntityType === "project" || this.recipientEntityType === "project" @@ -455,14 +449,14 @@ export default class GiftedDialog extends Vue { if (contact) { this.giver = { did: contact.did, - name: contact.name || contact.did, + name: contact.name, }; } else { // Only set to "Unnamed" if no giver is currently set if (!this.giver || !this.giver.did) { this.giver = { did: "", - name: "Unnamed", + name: UNNAMED_ENTITY_NAME, }; } } @@ -517,14 +511,14 @@ export default class GiftedDialog extends Vue { if (contact) { this.receiver = { did: contact.did, - name: contact.name || contact.did, + name: contact.name, }; } else { // Only set to "Unnamed" if no receiver is currently set if (!this.receiver || !this.receiver.did) { this.receiver = { did: "", - name: "Unnamed", + name: UNNAMED_ENTITY_NAME, }; } } @@ -566,20 +560,21 @@ export default class GiftedDialog extends Vue { /** * Handle entity selection from EntitySelectionStep - * @param entity - The selected entity (person, project, or special) with stepType + * @param entity - The selected entity (person or project) with stepType */ handleEntitySelected(entity: { - type: "person" | "project" | "special"; - entityType?: string; - data: Contact | PlanData | { did?: string; name: string }; + type: "person" | "project"; + data: Contact | PlanData; stepType: string; }) { if (entity.type === "person") { const contact = entity.data as Contact; + // Apply DID-based logic for person entities + const processedContact = this.processPersonEntity(contact); if (entity.stepType === "giver") { - this.selectGiver(contact); + this.selectGiver(processedContact); } else { - this.selectRecipient(contact); + this.selectRecipient(processedContact); } } else if (entity.type === "project") { const project = entity.data as PlanData; @@ -588,33 +583,22 @@ export default class GiftedDialog extends Vue { } else { this.selectRecipientProject(project); } - } else if (entity.type === "special") { - // Handle special entities like "You" and "Unnamed" - if (entity.entityType === "you") { - // "You" entity selected - const youEntity = { - did: this.activeDid, - name: "You", - }; - if (entity.stepType === "giver") { - this.giver = youEntity; - } else { - this.receiver = youEntity; - } - this.firstStep = false; - } else if (entity.entityType === "unnamed") { - // "Unnamed" entity selected - const unnamedEntity = { - did: "", - name: "Unnamed", - }; - if (entity.stepType === "giver") { - this.giver = unnamedEntity; - } else { - this.receiver = unnamedEntity; - } - this.firstStep = false; - } + } + } + + /** + * Processes person entities using DID-based logic for "You" and "Unnamed" + */ + private processPersonEntity(contact: Contact): Contact { + if (contact.did === this.activeDid) { + // If DID matches active DID, create "You" entity + return { ...contact, name: "You" }; + } else if (!contact.did || contact.did === "") { + // If DID is empty/null, create "Unnamed" entity + return { ...contact, name: UNNAMED_ENTITY_NAME }; + } else { + // Return the contact as-is + return contact; } } @@ -622,7 +606,10 @@ export default class GiftedDialog extends Vue { * Handle edit entity request from GiftDetailsStep * @param data - Object containing entityType and currentEntity */ - handleEditEntity(data: { entityType: string; currentEntity: any }) { + handleEditEntity(data: { + entityType: string; + currentEntity: { did: string; name: string }; + }) { this.goBackToStep1(data.entityType); } @@ -662,27 +649,3 @@ export default class GiftedDialog extends Vue { } } </script> - -<style> -.dialog-overlay { - z-index: 50; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 500px; -} -</style> diff --git a/src/components/GiftedPrompts.vue b/src/components/GiftedPrompts.vue index f4387472..8f5f72c9 100644 --- a/src/components/GiftedPrompts.vue +++ b/src/components/GiftedPrompts.vue @@ -291,27 +291,3 @@ export default class GivenPrompts extends Vue { } } </script> - -<style> -.dialog-overlay { - z-index: 50; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 500px; -} -</style> diff --git a/src/components/HiddenDidDialog.vue b/src/components/HiddenDidDialog.vue index 4b536e21..dfbc352d 100644 --- a/src/components/HiddenDidDialog.vue +++ b/src/components/HiddenDidDialog.vue @@ -1,9 +1,6 @@ <template> - <div - v-if="isOpen" - class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" - > - <div class="bg-white rounded-lg p-6 max-w-2xl w-full mx-4"> + <div v-if="isOpen" class="dialog-overlay"> + <div class="dialog"> <!-- Header --> <div class="flex justify-between items-center mb-4"> <h2 class="text-xl font-bold capitalize">{{ roleName }} Details</h2> diff --git a/src/components/IconRenderer.vue b/src/components/IconRenderer.vue deleted file mode 100644 index 83a0b14c..00000000 --- a/src/components/IconRenderer.vue +++ /dev/null @@ -1,90 +0,0 @@ -<template> - <svg - v-if="iconData" - :class="svgClass" - :fill="fill" - :stroke="stroke" - :viewBox="viewBox" - xmlns="http://www.w3.org/2000/svg" - > - <path v-for="(path, index) in iconData.paths" :key="index" v-bind="path" /> - </svg> -</template> - -<script lang="ts"> -import { Component, Prop, Vue } from "vue-facing-decorator"; -import icons from "../assets/icons.json"; -import { logger } from "../utils/logger"; - -/** - * Icon path interface - */ -interface IconPath { - d: string; - fillRule?: string; - clipRule?: string; - strokeLinecap?: string; - strokeLinejoin?: string; - strokeWidth?: string | number; - fill?: string; - stroke?: string; -} - -/** - * Icon data interface - */ -interface IconData { - paths: IconPath[]; -} - -/** - * Icons JSON structure - */ -interface IconsJson { - [key: string]: IconPath | IconData; -} - -/** - * Icon Renderer Component - * - * This component loads SVG icon definitions from a JSON file and renders them - * as SVG elements. It provides a clean way to use icons without cluttering - * templates with long SVG path definitions. - * - * @author Matthew Raymer - * @version 1.0.0 - * @since 2024 - */ -@Component({ - name: "IconRenderer", -}) -export default class IconRenderer extends Vue { - @Prop({ required: true }) readonly iconName!: string; - @Prop({ default: "h-5 w-5" }) readonly svgClass!: string; - @Prop({ default: "none" }) readonly fill!: string; - @Prop({ default: "currentColor" }) readonly stroke!: string; - @Prop({ default: "0 0 24 24" }) readonly viewBox!: string; - - /** - * Get the icon data for the specified icon name - * - * @returns {IconData | null} The icon data object or null if not found - */ - get iconData(): IconData | null { - const icon = (icons as IconsJson)[this.iconName]; - if (!icon) { - logger.warn(`Icon "${this.iconName}" not found in icons.json`); - return null; - } - - // Convert single path to array format for consistency - if ("d" in icon) { - return { - paths: [icon as IconPath], - }; - } - - return icon as IconData; - } -} -</script> diff --git a/src/components/ImageMethodDialog.vue b/src/components/ImageMethodDialog.vue index b6cdd9bd..61c3cf79 100644 --- a/src/components/ImageMethodDialog.vue +++ b/src/components/ImageMethodDialog.vue @@ -1,5 +1,5 @@ <template> - <div v-if="visible" class="dialog-overlay z-[60]"> + <div v-if="visible" class="dialog-overlay"> <div class="dialog relative"> <div class="text-lg text-center font-bold relative"> <h1 id="ViewHeading" class="text-center font-bold"> @@ -282,7 +282,7 @@ import { NOTIFY_IMAGE_DIALOG_UNSUPPORTED_FORMAT, createImageDialogCameraErrorMessage, } from "../constants/notifications"; -import { createNotifyHelpers, TIMEOUTS } from "../utils/notify"; +import { createNotifyHelpers, TIMEOUTS, NotifyFunction } from "../utils/notify"; const inputImageFileNameRef = ref<Blob>(); @@ -291,7 +291,7 @@ const inputImageFileNameRef = ref<Blob>(); mixins: [PlatformServiceMixin], }) export default class ImageMethodDialog extends Vue { - $notify!: (notification: any, timeout?: number) => void; + $notify!: NotifyFunction; $router!: Router; notify = createNotifyHelpers(this.$notify); @@ -931,32 +931,6 @@ export default class ImageMethodDialog extends Vue { </script> <style> -.dialog-overlay { - z-index: 50; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 700px; - max-height: 90vh; - overflow: hidden; - display: flex; - flex-direction: column; -} - /* Add styles for diagnostic panel */ .diagnostic-panel { font-family: monospace; diff --git a/src/components/InviteDialog.vue b/src/components/InviteDialog.vue index ed640c6e..d10a6f5b 100644 --- a/src/components/InviteDialog.vue +++ b/src/components/InviteDialog.vue @@ -93,27 +93,3 @@ export default class InviteDialog extends Vue { } } </script> - -<style> -.dialog-overlay { - z-index: 50; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 500px; -} -</style> diff --git a/src/components/LocationSearchSection.vue b/src/components/LocationSearchSection.vue index 1e0f59bb..c39373b6 100644 --- a/src/components/LocationSearchSection.vue +++ b/src/components/LocationSearchSection.vue @@ -26,7 +26,7 @@ :weight="2" color="#3b82f6" fill-color="#3b82f6" - fill-opacity="0.2" + :fill-opacity="0.2" /> </l-map> </div> diff --git a/src/components/MembersList.vue b/src/components/MembersList.vue index 89840ebd..ed6b1a32 100644 --- a/src/components/MembersList.vue +++ b/src/components/MembersList.vue @@ -81,7 +81,7 @@ <div class="flex items-center justify-between"> <div class="flex items-center"> <h3 class="text-lg font-medium"> - {{ member.name || "Unnamed Member" }} + {{ member.name || unnamedMember }} </h3> <div v-if="!getContactFor(member.did) && member.did !== activeDid" @@ -177,6 +177,7 @@ import { NOTIFY_ADD_CONTACT_FIRST, NOTIFY_CONTINUE_WITHOUT_ADDING, } from "@/constants/notifications"; +import { SOMEONE_UNNAMED } from "@/constants/entities"; interface Member { admitted: boolean; @@ -220,6 +221,13 @@ export default class MembersList extends Vue { apiServer = ""; contacts: Array<Contact> = []; + /** + * Get the unnamed member constant + */ + get unnamedMember(): string { + return SOMEONE_UNNAMED; + } + async created() { this.notify = createNotifyHelpers(this.$notify); diff --git a/src/components/OfferDialog.vue b/src/components/OfferDialog.vue index 81664088..a958bd06 100644 --- a/src/components/OfferDialog.vue +++ b/src/components/OfferDialog.vue @@ -312,28 +312,3 @@ export default class OfferDialog extends Vue { } } </script> - -<style scoped> -.dialog-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - z-index: 50; -} - -.dialog { - background: white; - padding: 1.5rem; - border-radius: 0.5rem; - max-width: 500px; - width: 90%; - max-height: 90vh; - overflow-y: auto; -} -</style> diff --git a/src/components/OnboardingDialog.vue b/src/components/OnboardingDialog.vue index f6275f12..fe419d55 100644 --- a/src/components/OnboardingDialog.vue +++ b/src/components/OnboardingDialog.vue @@ -307,27 +307,3 @@ export default class OnboardingDialog extends Vue { } } </script> - -<style> -.dialog-overlay { - z-index: 40; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 500px; -} -</style> diff --git a/src/components/PersonCard.vue b/src/components/PersonCard.vue index 1f3bf595..bf2b72b9 100644 --- a/src/components/PersonCard.vue +++ b/src/components/PersonCard.vue @@ -25,7 +25,7 @@ conflict detection. * * @author Matthew Raymer */ </div> <h3 :class="nameClasses"> - {{ person.name || person.did || "Unnamed" }} + {{ displayName }} </h3> </li> </template> @@ -98,9 +98,27 @@ export default class PersonCard extends Vue { return `${baseClasses} text-slate-400`; } + // Add italic styling for entities without set names + if (!this.person.name) { + return `${baseClasses} italic text-slate-500`; + } + return baseClasses; } + /** + * Computed display name for the person + */ + get displayName(): string { + // If the entity has a set name, use that name + if (this.person.name) { + return this.person.name; + } + + // If the entity does not have a set name + return this.person.did; + } + /** * Handle card click - emit if selectable and not conflicted, show warning if conflicted */ @@ -114,7 +132,7 @@ export default class PersonCard extends Vue { group: "alert", type: "warning", title: "Cannot Select", - text: `You cannot select "${this.person.name || this.person.did || "Unnamed"}" because they are already selected as the ${this.conflictContext}.`, + text: `You cannot select "${this.displayName}" because they are already selected as the ${this.conflictContext}.`, }, 3000, ); diff --git a/src/components/PhotoDialog.vue b/src/components/PhotoDialog.vue index 590f60b3..54511f67 100644 --- a/src/components/PhotoDialog.vue +++ b/src/components/PhotoDialog.vue @@ -10,7 +10,7 @@ Comprehensive error handling * * @author Matthew Raymer * @version 1.0.0 * @file PhotoDialog.vue */ <template> - <div v-if="visible" class="dialog-overlay z-[60]"> + <div v-if="visible" class="dialog-overlay"> <div class="dialog relative"> <div class="text-lg text-center font-light relative z-50"> <div id="ViewHeading" :class="headingClasses"> @@ -628,34 +628,6 @@ export default class PhotoDialog extends Vue { </script> <style> -/* Dialog overlay styling */ -.dialog-overlay { - z-index: 60; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - -/* Dialog container styling */ -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 700px; - max-height: 90vh; - overflow: hidden; - display: flex; - flex-direction: column; -} - /* Camera preview styling */ .camera-preview { flex: 1; diff --git a/src/components/ProjectCard.vue b/src/components/ProjectCard.vue index 7f09eda8..f0ba9e6c 100644 --- a/src/components/ProjectCard.vue +++ b/src/components/ProjectCard.vue @@ -15,7 +15,7 @@ issuer information. * * @author Matthew Raymer */ <h3 class="text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden" > - {{ project.name || "Unnamed Project" }} + {{ project.name || unnamedProject }} </h3> <div class="text-xs text-slate-500 truncate"> @@ -31,6 +31,7 @@ import ProjectIcon from "./ProjectIcon.vue"; import { PlanData } from "../interfaces/records"; import { Contact } from "../db/tables/contacts"; import { didInfo } from "../libs/endorserServer"; +import { UNNAMED_PROJECT } from "@/constants/entities"; /** * ProjectCard - Displays a project entity with selection capability @@ -63,6 +64,13 @@ export default class ProjectCard extends Vue { @Prop({ required: true }) allContacts!: Contact[]; + /** + * Get the unnamed project constant + */ + get unnamedProject(): string { + return UNNAMED_PROJECT; + } + /** * Computed display name for the project issuer */ diff --git a/src/components/PushNotificationPermission.vue b/src/components/PushNotificationPermission.vue index 7372c63d..37b266a0 100644 --- a/src/components/PushNotificationPermission.vue +++ b/src/components/PushNotificationPermission.vue @@ -115,6 +115,7 @@ import { urlBase64ToUint8Array } from "../libs/crypto/vc/util"; import * as libsUtil from "../libs/util"; import { logger } from "../utils/logger"; import { PlatformServiceMixin } from "../utils/PlatformServiceMixin"; +import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; // Example interface for error interface ErrorResponse { @@ -602,7 +603,7 @@ export default class PushNotificationPermission extends Vue { * Returns the default message for direct push */ get notificationMessagePlaceholder(): string { - return "Click to share some gratitude with the world -- even if they're unnamed."; + return `Click to share some gratitude with the world -- even if they're ${UNNAMED_ENTITY_NAME.toLowerCase()}.`; } /** diff --git a/src/components/QuickNav.vue b/src/components/QuickNav.vue index fadd4991..133b1553 100644 --- a/src/components/QuickNav.vue +++ b/src/components/QuickNav.vue @@ -2,7 +2,7 @@ <!-- QUICK NAV --> <nav id="QuickNav" - class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50 pb-[env(safe-area-inset-bottom)]" + class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50 pb-[max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px))]" > <ul class="flex text-2xl px-6 py-2 gap-1 max-w-3xl mx-auto"> <!-- Home Feed --> diff --git a/src/components/RegistrationNotice.vue b/src/components/RegistrationNotice.vue index c9faffea..683736ff 100644 --- a/src/components/RegistrationNotice.vue +++ b/src/components/RegistrationNotice.vue @@ -1,33 +1,154 @@ +/** * @file RegistrationNotice.vue * @description Reusable component for +displaying user registration status and related actions. * Shows registration +notice when user is not registered, with options to show identifier info * or +access advanced options. * * @author Jose Olarte III * @version 1.0.0 * @created +2025-08-21T17:25:28-08:00 */ + <template> <div - v-if="!isRegistered && show" - id="noticeBeforeAnnounce" - class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 mt-4" - role="alert" - aria-live="polite" + id="noticeSomeoneMustRegisterYou" + class="bg-amber-200 text-amber-900 border-amber-500 border-dashed border text-center rounded-md overflow-hidden px-4 py-3 my-4" > - <p class="mb-4"> - Before you can publicly announce a new project or time commitment, a - friend needs to register you. - </p> - <button - class="inline-block text-md bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" - @click="shareInfo" - > - Share Your Info - </button> + <p class="mb-4">{{ message }}</p> + <div class="grid grid-cols-1 gap-2 sm:flex sm:justify-center"> + <button + class="inline-block text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" + @click="showNameThenIdDialog" + > + Show them {{ passkeysEnabled ? "default" : "your" }} identifier info + </button> + <button + class="inline-block text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md" + @click="openAdvancedOptions" + > + See advanced options + </button> + </div> </div> + <UserNameDialog ref="userNameDialog" /> + <ChoiceButtonDialog ref="choiceButtonDialog" /> </template> <script lang="ts"> -import { Component, Vue, Prop, Emit } from "vue-facing-decorator"; +import { Component, Vue, Prop } from "vue-facing-decorator"; +import { Router } from "vue-router"; +import { Capacitor } from "@capacitor/core"; +import UserNameDialog from "./UserNameDialog.vue"; +import ChoiceButtonDialog from "./ChoiceButtonDialog.vue"; -@Component({ name: "RegistrationNotice" }) +/** + * RegistrationNotice Component + * + * Displays registration status notice and provides actions for unregistered users. + * Handles all registration-related flows internally without requiring parent component intervention. + * + * Template Usage: + * ```vue + * <RegistrationNotice + * v-if="!isUserRegistered" + * :passkeys-enabled="PASSKEYS_ENABLED" + * :given-name="givenName" + * message="Custom registration message here" + * /> + * ``` + * + * Component Dependencies: + * - UserNameDialog: Dialog for entering user name + * - ChoiceButtonDialog: Dialog for sharing method selection + */ +@Component({ + name: "RegistrationNotice", + components: { + UserNameDialog, + ChoiceButtonDialog, + }, +}) export default class RegistrationNotice extends Vue { - @Prop({ required: true }) isRegistered!: boolean; - @Prop({ required: true }) show!: boolean; + $router!: Router; + + /** + * Whether passkeys are enabled in the application + */ + @Prop({ required: true }) + passkeysEnabled!: boolean; + + /** + * User's given name for dialog pre-population + */ + @Prop({ required: true }) + givenName!: string; + + /** + * Custom message to display in the registration notice + * Defaults to "To share, someone must register you." + */ + @Prop({ default: "To share, someone must register you." }) + message!: string; + + /** + * Shows name input dialog if needed + * Handles the full flow internally without requiring parent component intervention + */ + showNameThenIdDialog() { + this.openUserNameDialog(() => { + this.promptForShareMethod(); + }); + } + + /** + * Opens advanced options page + * Navigates directly to the start page + */ + openAdvancedOptions() { + this.$router.push({ name: "start" }); + } + + /** + * Shows dialog for sharing method selection + * Provides options for different sharing scenarios + */ + promptForShareMethod() { + (this.$refs.choiceButtonDialog as ChoiceButtonDialog).open({ + title: "How can you share your info?", + text: "", + option1Text: "We are nearby with cameras", + option2Text: "Someone created a meeting room", + option3Text: "We will share some other way", + onOption1: () => { + this.handleQRCodeClick(); + }, + onOption2: () => { + this.$router.push({ name: "onboard-meeting-list" }); + }, + onOption3: () => { + this.$router.push({ name: "share-my-contact-info" }); + }, + }); + } + + /** + * Handles QR code sharing based on platform + * Navigates to appropriate QR code page + */ + private handleQRCodeClick() { + if (Capacitor.isNativePlatform()) { + this.$router.push({ name: "contact-qr-scan-full" }); + } else { + this.$router.push({ name: "contact-qr" }); + } + } - @Emit("share-info") - shareInfo() {} + /** + * Opens the user name dialog if needed + * + * @param callback Function to call after name is entered + */ + openUserNameDialog(callback: () => void) { + if (!this.givenName) { + (this.$refs.userNameDialog as UserNameDialog).open(callback); + } else { + callback(); + } + } } </script> diff --git a/src/components/SpecialEntityCard.vue b/src/components/SpecialEntityCard.vue index 1d475229..e489d003 100644 --- a/src/components/SpecialEntityCard.vue +++ b/src/components/SpecialEntityCard.vue @@ -124,8 +124,6 @@ export default class SpecialEntityCard extends Vue { handleClick(): void { if (this.selectable && !this.conflicted) { this.emitEntitySelected({ - type: "special", - entityType: this.entityType, data: this.entityData, }); } else if (this.conflicted && this.notify) { @@ -145,13 +143,7 @@ export default class SpecialEntityCard extends Vue { // Emit methods using @Emit decorator @Emit("entity-selected") - emitEntitySelected(data: { - type: string; - entityType: string; - data: { did?: string; name: string }; - }): { - type: string; - entityType: string; + emitEntitySelected(data: { data: { did?: string; name: string } }): { data: { did?: string; name: string }; } { return data; diff --git a/src/components/TopMessage.vue b/src/components/TopMessage.vue index 519a0585..a884af74 100644 --- a/src/components/TopMessage.vue +++ b/src/components/TopMessage.vue @@ -1,5 +1,7 @@ <template> - <div class="absolute right-5 top-[max(0.75rem,env(safe-area-inset-top))]"> + <div + class="absolute right-5 top-[max(0.75rem,env(safe-area-inset-top),var(--safe-area-inset-top,0px))]" + > <span class="align-center text-red-500 mr-2">{{ message }}</span> <span class="ml-2"> <router-link @@ -18,6 +20,7 @@ import { Component, Vue, Prop } from "vue-facing-decorator"; import { AppString, NotificationIface } from "../constants/app"; import { PlatformServiceMixin } from "../utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "../utils/notify"; +import { logger } from "../utils/logger"; @Component({ mixins: [PlatformServiceMixin], @@ -42,26 +45,49 @@ export default class TopMessage extends Vue { this.notify = createNotifyHelpers(this.$notify); try { - // Ultra-concise cached settings loading - replaces 50+ lines of logic! - const settings = await this.$accountSettings(undefined, { - activeDid: undefined, - apiServer: AppString.PROD_ENDORSER_API_SERVER, + // Load settings without overriding database values - fixes settings inconsistency + logger.debug("[TopMessage] 📥 Loading settings without overrides..."); + const settings = await this.$accountSettings(); + + logger.debug("[TopMessage] 📊 Settings loaded:", { + activeDid: settings.activeDid, + apiServer: settings.apiServer, + warnIfTestServer: settings.warnIfTestServer, + warnIfProdServer: settings.warnIfProdServer, + component: "TopMessage", + timestamp: new Date().toISOString(), }); + // Only show warnings if the user has explicitly enabled them if ( settings.warnIfTestServer && + settings.apiServer && settings.apiServer !== AppString.PROD_ENDORSER_API_SERVER ) { const didPrefix = settings.activeDid?.slice(11, 15); this.message = "You're not using prod, user " + didPrefix; + logger.debug("[TopMessage] ⚠️ Test server warning displayed:", { + apiServer: settings.apiServer, + didPrefix: didPrefix, + }); } else if ( settings.warnIfProdServer && + settings.apiServer && settings.apiServer === AppString.PROD_ENDORSER_API_SERVER ) { const didPrefix = settings.activeDid?.slice(11, 15); this.message = "You are using prod, user " + didPrefix; + logger.debug("[TopMessage] ⚠️ Production server warning displayed:", { + apiServer: settings.apiServer, + didPrefix: didPrefix, + }); + } else { + logger.debug( + "[TopMessage] ℹ️ No warnings displayed - conditions not met", + ); } } catch (err: unknown) { + logger.error("[TopMessage] ❌ Error loading settings:", err); this.notify.error(JSON.stringify(err), TIMEOUTS.MODAL); } } diff --git a/src/components/UsageLimitsSection.vue b/src/components/UsageLimitsSection.vue index 4eb9d8ef..ed53393d 100644 --- a/src/components/UsageLimitsSection.vue +++ b/src/components/UsageLimitsSection.vue @@ -83,6 +83,7 @@ <script lang="ts"> import { Component, Vue, Prop } from "vue-facing-decorator"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; +import { EndorserRateLimits, ImageRateLimits } from "@/interfaces/limits"; @Component({ name: "UsageLimitsSection", @@ -94,8 +95,8 @@ export default class UsageLimitsSection extends Vue { @Prop({ required: true }) loadingLimits!: boolean; @Prop({ required: true }) limitsMessage!: string; @Prop({ required: false }) activeDid?: string; - @Prop({ required: false }) endorserLimits?: any; - @Prop({ required: false }) imageLimits?: any; + @Prop({ required: false }) endorserLimits?: EndorserRateLimits; + @Prop({ required: false }) imageLimits?: ImageRateLimits; @Prop({ required: true }) onRecheckLimits!: () => void; mounted() { diff --git a/src/components/UserNameDialog.vue b/src/components/UserNameDialog.vue index dec1a84c..7a426e7f 100644 --- a/src/components/UserNameDialog.vue +++ b/src/components/UserNameDialog.vue @@ -84,7 +84,8 @@ export default class UserNameDialog extends Vue { */ async open(aCallback?: (name?: string) => void) { this.callback = aCallback || this.callback; - const settings = await this.$settings(); + // Load from account-specific settings instead of master settings + const settings = await this.$accountSettings(); this.givenName = settings.firstName || ""; this.visible = true; } @@ -95,7 +96,18 @@ export default class UserNameDialog extends Vue { */ async onClickSaveChanges() { try { - await this.$updateSettings({ firstName: this.givenName }); + // Get the current active DID to save to user-specific settings + const settings = await this.$accountSettings(); + const activeDid = settings.activeDid; + + if (activeDid) { + // Save to user-specific settings for the current identity + await this.$saveUserSettings(activeDid, { firstName: this.givenName }); + } else { + // Fallback to master settings if no active DID + await this.$saveSettings({ firstName: this.givenName }); + } + this.visible = false; this.callback(this.givenName); } catch (error) { @@ -134,27 +146,3 @@ export default class UserNameDialog extends Vue { } } </script> - -<style> -.dialog-overlay { - z-index: 50; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} - -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 500px; -} -</style> diff --git a/src/constants/entities.ts b/src/constants/entities.ts new file mode 100644 index 00000000..62b9af5a --- /dev/null +++ b/src/constants/entities.ts @@ -0,0 +1,14 @@ +/** + * Constants for entity-related strings, particularly for unnamed/unknown person entities + */ + +// Core unnamed entity names +export const UNNAMED_ENTITY_NAME = "Unnamed"; + +// Descriptive phrases for unnamed entities +export const SOMEONE_UNNAMED = "Someone Unnamed"; +export const THAT_UNNAMED_PERSON = "That unnamed person"; +export const UNNAMED_PERSON = "unnamed person"; + +// Project-related unnamed entities +export const UNNAMED_PROJECT = "Unnamed Project"; diff --git a/src/constants/notifications.ts b/src/constants/notifications.ts index 474e0d5e..5cc75bd4 100644 --- a/src/constants/notifications.ts +++ b/src/constants/notifications.ts @@ -1,4 +1,5 @@ import axios from "axios"; +import { THAT_UNNAMED_PERSON } from "./entities"; // Notification message constants for user-facing notifications // Add new notification messages here as needed @@ -846,6 +847,12 @@ export const NOTIFY_CONTACTS_ADDED = { message: "They were added.", }; +// Used in: ContactsView.vue (addContact method - export data prompt after contact addition) +export const NOTIFY_EXPORT_DATA_PROMPT = { + title: "Export Your Data", + message: "Would you like to export your contact data as a backup?", +}; + // Used in: ContactsView.vue (showCopySelectionsInfo method - info about copying contacts) export const NOTIFY_CONTACT_INFO_COPY = { title: "Info", @@ -867,7 +874,7 @@ export const NOTIFY_CONTACT_LINK_COPIED = { // Template for registration success message // Used in: ContactsView.vue (register method - registration success with contact name) export const getRegisterPersonSuccessMessage = (name?: string): string => - `${name || "That unnamed person"} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`; + `${name || THAT_UNNAMED_PERSON} ${NOTIFY_REGISTER_PERSON_SUCCESS.message}`; // Template for visibility success message // Used in: ContactsView.vue (setVisibility method - visibility success with contact name) @@ -1372,7 +1379,7 @@ export function createQRContactAddedMessage(hasVisibility: boolean): string { export function createQRRegistrationSuccessMessage( contactName: string, ): string { - return `${contactName || "That unnamed person"}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`; + return `${contactName || THAT_UNNAMED_PERSON}${NOTIFY_QR_REGISTRATION_SUCCESS.message}`; } // ContactQRScanShowView.vue timeout constants @@ -1588,7 +1595,7 @@ export function createImageDialogCameraErrorMessage(error: Error): string { // Helper function for dynamic upload error messages // Used in: ImageMethodDialog.vue (uploadImage method - dynamic upload error message) -export function createImageDialogUploadErrorMessage(error: any): string { +export function createImageDialogUploadErrorMessage(error: unknown): string { if (axios.isAxiosError(error)) { const status = error.response?.status; const data = error.response?.data; @@ -1682,3 +1689,11 @@ export const NOTIFY_CONTACTS_ADDED_CONFIRM = { title: "They're Added To Your List", message: "Would you like to go to the main page now?", }; + +// ImportAccountView.vue specific constants +// Used in: ImportAccountView.vue (onImportClick method - duplicate account warning) +export const NOTIFY_DUPLICATE_ACCOUNT_IMPORT = { + title: "Account Already Imported", + message: + "This account has already been imported. Please use a different seed phrase or check your existing accounts.", +}; diff --git a/src/interfaces/common.ts b/src/interfaces/common.ts index 7aca4120..b2e68d1f 100644 --- a/src/interfaces/common.ts +++ b/src/interfaces/common.ts @@ -60,9 +60,13 @@ export interface AxiosErrorResponse { [key: string]: unknown; }; status?: number; + statusText?: string; config?: unknown; }; - config?: unknown; + config?: { + url?: string; + [key: string]: unknown; + }; [key: string]: unknown; } @@ -98,3 +102,81 @@ export interface VerifiableCredentialClaim { credentialSubject: ClaimObject; [key: string]: unknown; } + +/** + * Database constraint error types for consistent error handling + */ +export interface DatabaseConstraintError extends Error { + name: "ConstraintError"; + message: string; + constraint?: string; +} + +/** + * Database storage error types for IndexedDB/SQLite operations + */ +export interface DatabaseStorageError extends Error { + name: "StorageError"; + message: string; + code?: string; + constraint?: string; +} + +/** + * Legacy Dexie error types for migration compatibility + */ +export interface DexieError extends Error { + name: string; + message: string; + inner?: unknown; + stack?: string; +} + +/** + * Type guard for database constraint errors + */ +export function isDatabaseConstraintError( + error: unknown, +): error is DatabaseConstraintError { + return error instanceof Error && error.name === "ConstraintError"; +} + +/** + * Type guard for database storage errors + */ +export function isDatabaseStorageError( + error: unknown, +): error is DatabaseStorageError { + return error instanceof Error && error.name === "StorageError"; +} + +/** + * Type guard for legacy Dexie errors + */ +export function isDexieError(error: unknown): error is DexieError { + return ( + error instanceof Error && + (error.name === "DexieError" || + error.message.includes("Key already exists in the object store") || + error.message.includes("ConstraintError")) + ); +} + +/** + * Unified error type for database operations + */ +export type DatabaseError = + | DatabaseConstraintError + | DatabaseStorageError + | DexieError; + +/** + * Type guard for any database error + */ +export function isDatabaseError(error: unknown): error is DatabaseError { + return ( + isDatabaseConstraintError(error) || + isDatabaseStorageError(error) || + isDexieError(error) + ); +} diff --git a/src/interfaces/deepLinks.ts b/src/interfaces/deepLinks.ts index d5266c7a..0fe5c68d 100644 --- a/src/interfaces/deepLinks.ts +++ b/src/interfaces/deepLinks.ts @@ -28,7 +28,7 @@ import { z } from "zod"; // Parameter validation schemas for each route type -export const deepLinkSchemas = { +export const deepLinkPathSchemas = { claim: z.object({ id: z.string(), }), @@ -60,7 +60,7 @@ export const deepLinkSchemas = { jwt: z.string().optional(), }), "onboard-meeting-members": z.object({ - id: z.string(), + groupId: z.string(), }), project: z.object({ id: z.string(), @@ -70,6 +70,17 @@ export const deepLinkSchemas = { }), }; +export const deepLinkQuerySchemas = { + "onboard-meeting-members": z.object({ + password: z.string(), + }), +}; + +// Add a union type of all valid route paths +export const VALID_DEEP_LINK_ROUTES = Object.keys( + deepLinkPathSchemas, +) as readonly (keyof typeof deepLinkPathSchemas)[]; + // Create a type from the array export type DeepLinkRoute = (typeof VALID_DEEP_LINK_ROUTES)[number]; @@ -80,14 +91,13 @@ export const baseUrlSchema = z.object({ queryParams: z.record(z.string()).optional(), }); -// Add a union type of all valid route paths -export const VALID_DEEP_LINK_ROUTES = Object.keys( - deepLinkSchemas, -) as readonly (keyof typeof deepLinkSchemas)[]; +// export type DeepLinkPathParams = { +// [K in keyof typeof deepLinkPathSchemas]: z.infer<(typeof deepLinkPathSchemas)[K]>; +// }; -export type DeepLinkParams = { - [K in keyof typeof deepLinkSchemas]: z.infer<(typeof deepLinkSchemas)[K]>; -}; +// export type DeepLinkQueryParams = { +// [K in keyof typeof deepLinkQuerySchemas]: z.infer<(typeof deepLinkQuerySchemas)[K]>; +// }; export interface DeepLinkError extends Error { code: string; diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts index cdc516b1..ca8a9e97 100644 --- a/src/libs/endorserServer.ts +++ b/src/libs/endorserServer.ts @@ -60,6 +60,7 @@ import { PlanSummaryRecord } from "../interfaces/records"; import { logger } from "../utils/logger"; import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { APP_SERVER } from "@/constants/app"; +import { SOMEONE_UNNAMED } from "@/constants/entities"; /** * Standard context for schema.org data @@ -309,7 +310,7 @@ export function didInfoForContact( showDidForVisible: boolean = false, // eslint-disable-next-line @typescript-eslint/no-explicit-any ): { known: boolean; displayName: string; profileImageUrl?: string } { - if (!did) return { displayName: "Someone Unnamed/Unknown", known: false }; + if (!did) return { displayName: SOMEONE_UNNAMED, known: false }; if (did === activeDid) { return { displayName: "You", known: true }; } else if (contact) { @@ -485,6 +486,15 @@ const planCache: LRUCache<string, PlanSummaryRecord> = new LRUCache({ max: 500, }); +/** + * Tracks in-flight requests to prevent duplicate API calls for the same plan + * @constant {Map} + */ +const inFlightRequests = new Map< + string, + Promise<PlanSummaryRecord | undefined> +>(); + /** * Retrieves plan data from cache or server * @param {string} handleId - Plan handle ID @@ -504,40 +514,140 @@ export async function getPlanFromCache( if (!handleId) { return undefined; } - let cred = planCache.get(handleId); - if (!cred) { - const url = - apiServer + - "/api/v2/report/plans?handleId=" + - encodeURIComponent(handleId); - const headers = await getHeaders(requesterDid); - try { - const resp = await axios.get(url, { headers }); - if (resp.status === 200 && resp.data?.data?.length > 0) { - cred = resp.data.data[0]; - planCache.set(handleId, cred); - } else { - // Use debug level for development to reduce console noise - const isDevelopment = process.env.VITE_PLATFORM === "development"; - const log = isDevelopment ? logger.debug : logger.log; - - log( - "[EndorserServer] Plan cache is empty for handle", - handleId, - " Got data:", - JSON.stringify(resp.data), - ); - } - } catch (error) { - logger.error( - "[EndorserServer] Failed to load plan with handle", + + // Check cache first (existing behavior) + const cred = planCache.get(handleId); + if (cred) { + return cred; + } + + // Check if request is already in flight (NEW: request deduplication) + if (inFlightRequests.has(handleId)) { + logger.debug( + "[Plan Loading] 🔄 Request already in flight, reusing promise:", + { + handleId, + requesterDid, + timestamp: new Date().toISOString(), + }, + ); + return inFlightRequests.get(handleId); + } + + // Create new request promise (NEW: request coordination) + const requestPromise = performPlanRequest( + handleId, + axios, + apiServer, + requesterDid, + ); + inFlightRequests.set(handleId, requestPromise); + + try { + const result = await requestPromise; + return result; + } finally { + // Clean up in-flight request tracking (NEW: cleanup) + inFlightRequests.delete(handleId); + } +} + +/** + * Performs the actual plan request to the server + * @param {string} handleId - Plan handle ID + * @param {Axios} axios - Axios instance + * @param {string} apiServer - API server URL + * @param {string} [requesterDid] - Optional requester DID for private info + * @returns {Promise<PlanSummaryRecord|undefined>} Plan data or undefined if not found + * + * @throws {Error} If server request fails + */ +async function performPlanRequest( + handleId: string, + axios: Axios, + apiServer: string, + requesterDid?: string, +): Promise<PlanSummaryRecord | undefined> { + const url = + apiServer + "/api/v2/report/plans?handleId=" + encodeURIComponent(handleId); + const headers = await getHeaders(requesterDid); + + // Enhanced diagnostic logging for plan loading + const requestId = `plan_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + logger.debug("[Plan Loading] 🔍 Loading plan from server:", { + requestId, + handleId, + apiServer, + endpoint: url, + requesterDid, + timestamp: new Date().toISOString(), + }); + + try { + const resp = await axios.get(url, { headers }); + + logger.debug("[Plan Loading] ✅ Plan loaded successfully:", { + requestId, + handleId, + status: resp.status, + hasData: !!resp.data?.data, + dataLength: resp.data?.data?.length || 0, + timestamp: new Date().toISOString(), + }); + + if (resp.status === 200 && resp.data?.data?.length > 0) { + const cred = resp.data.data[0]; + planCache.set(handleId, cred); + + logger.debug("[Plan Loading] 💾 Plan cached:", { + requestId, + handleId, + planName: cred?.name, + planIssuer: cred?.issuerDid, + }); + + return cred; + } else { + // Use debug level for development to reduce console noise + const isDevelopment = process.env.VITE_PLATFORM === "development"; + const log = isDevelopment ? logger.debug : logger.log; + + log( + "[Plan Loading] ⚠️ Plan cache is empty for handle", handleId, - " Got error:", - JSON.stringify(error), + " Got data:", + JSON.stringify(resp.data), ); + + return undefined; } + } catch (error) { + // Enhanced error logging for plan loading failures + const axiosError = error as { + response?: { + data?: unknown; + status?: number; + statusText?: string; + }; + message?: string; + }; + + logger.error("[Plan Loading] ❌ Failed to load plan:", { + requestId, + handleId, + apiServer, + endpoint: url, + requesterDid, + errorStatus: axiosError.response?.status, + errorStatusText: axiosError.response?.statusText, + errorData: axiosError.response?.data, + errorMessage: axiosError.message || String(error), + timestamp: new Date().toISOString(), + }); + + throw error; } - return cred; } /** @@ -1018,19 +1128,82 @@ export async function createAndSubmitClaim( const vcJwt: string = await createEndorserJwtForDid(issuerDid, vcPayload); + // Enhanced diagnostic logging for claim submission + const requestId = `claim_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + logger.info("[Claim Submission] 🚀 Starting claim submission:", { + requestId, + apiServer, + requesterDid: issuerDid, + endpoint: `${apiServer}/api/v2/claim`, + timestamp: new Date().toISOString(), + jwtLength: vcJwt.length, + }); + // Make the xhr request payload const payload = JSON.stringify({ jwtEncoded: vcJwt }); const url = `${apiServer}/api/v2/claim`; + logger.debug("[Claim Submission] 📡 Making API request:", { + requestId, + url, + payloadSize: payload.length, + headers: { "Content-Type": "application/json" }, + }); + const response = await axios.post(url, payload, { headers: { "Content-Type": "application/json", }, }); + logger.info("[Claim Submission] ✅ Claim submitted successfully:", { + requestId, + status: response.status, + handleId: response.data?.handleId, + responseSize: JSON.stringify(response.data).length, + timestamp: new Date().toISOString(), + }); + return { success: true, handleId: response.data?.handleId }; } catch (error: unknown) { - logger.error("Error submitting claim:", error); + // Enhanced error logging with comprehensive context + const requestId = `claim_error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + const axiosError = error as { + response?: { + data?: { error?: { code?: string; message?: string } }; + status?: number; + statusText?: string; + headers?: Record<string, string>; + }; + config?: { + url?: string; + method?: string; + headers?: Record<string, string>; + }; + message?: string; + }; + + logger.error("[Claim Submission] ❌ Claim submission failed:", { + requestId, + apiServer, + requesterDid: issuerDid, + endpoint: `${apiServer}/api/v2/claim`, + errorCode: axiosError.response?.data?.error?.code, + errorMessage: axiosError.response?.data?.error?.message, + httpStatus: axiosError.response?.status, + httpStatusText: axiosError.response?.statusText, + responseHeaders: axiosError.response?.headers, + requestConfig: { + url: axiosError.config?.url, + method: axiosError.config?.method, + headers: axiosError.config?.headers, + }, + originalError: axiosError.message || String(error), + timestamp: new Date().toISOString(), + }); + const errorMessage: string = serverMessageForUser(error) || (error && typeof error === "object" && "message" in error @@ -1515,14 +1688,56 @@ export async function fetchEndorserRateLimits( ) { const url = `${apiServer}/api/report/rateLimits`; const headers = await getHeaders(issuerDid); + + // Enhanced diagnostic logging for user registration tracking + logger.debug("[User Registration] Checking user status on server:", { + did: issuerDid, + server: apiServer, + endpoint: url, + timestamp: new Date().toISOString(), + }); + try { const response = await axios.get(url, { headers } as AxiosRequestConfig); + + // Log successful registration check + logger.debug("[User Registration] User registration check successful:", { + did: issuerDid, + server: apiServer, + status: response.status, + isRegistered: true, + timestamp: new Date().toISOString(), + }); + return response; } catch (error) { + // Enhanced error logging with user registration context + const axiosError = error as { + response?: { + data?: { error?: { code?: string; message?: string } }; + status?: number; + }; + }; + const errorCode = axiosError.response?.data?.error?.code; + const errorMessage = axiosError.response?.data?.error?.message; + const httpStatus = axiosError.response?.status; + + logger.warn("[User Registration] User not registered on server:", { + did: issuerDid, + server: apiServer, + errorCode: errorCode, + errorMessage: errorMessage, + httpStatus: httpStatus, + needsRegistration: true, + timestamp: new Date().toISOString(), + }); + + // Log the original error for debugging logger.error( `[fetchEndorserRateLimits] Error for DID ${issuerDid}:`, errorStringForLog(error), ); + throw error; } } @@ -1535,8 +1750,53 @@ export async function fetchEndorserRateLimits( * @param {string} issuerDid - The DID for which to check rate limits. * @returns {Promise<AxiosResponse>} The Axios response object. */ -export async function fetchImageRateLimits(axios: Axios, issuerDid: string) { - const url = DEFAULT_IMAGE_API_SERVER + "/image-limits"; +export async function fetchImageRateLimits( + axios: Axios, + issuerDid: string, + imageServer?: string, +) { + const server = imageServer || DEFAULT_IMAGE_API_SERVER; + const url = server + "/image-limits"; const headers = await getHeaders(issuerDid); - return await axios.get(url, { headers } as AxiosRequestConfig); + + // Enhanced diagnostic logging for image server calls + logger.debug("[Image Server] Checking image rate limits:", { + did: issuerDid, + server: server, + endpoint: url, + timestamp: new Date().toISOString(), + }); + + try { + const response = await axios.get(url, { headers } as AxiosRequestConfig); + + // Log successful image server call + logger.debug("[Image Server] Image rate limits check successful:", { + did: issuerDid, + server: server, + status: response.status, + timestamp: new Date().toISOString(), + }); + + return response; + } catch (error) { + // Enhanced error logging for image server failures + const axiosError = error as { + response?: { + data?: { error?: { code?: string; message?: string } }; + status?: number; + }; + }; + + logger.warn("[Image Server] Image rate limits check failed:", { + did: issuerDid, + server: server, + errorCode: axiosError.response?.data?.error?.code, + errorMessage: axiosError.response?.data?.error?.message, + httpStatus: axiosError.response?.status, + timestamp: new Date().toISOString(), + }); + + throw error; + } } diff --git a/src/libs/fontawesome.ts b/src/libs/fontawesome.ts index 8c36ee14..30c745c7 100644 --- a/src/libs/fontawesome.ts +++ b/src/libs/fontawesome.ts @@ -20,6 +20,7 @@ import { faCameraRotate, faCaretDown, faChair, + faChartLine, faCheck, faChevronDown, faChevronLeft, @@ -28,6 +29,7 @@ import { faCircle, faCircleCheck, faCircleInfo, + faCirclePlus, faCircleQuestion, faCircleRight, faCircleUser, @@ -49,6 +51,7 @@ import { faFloppyDisk, faFolderOpen, faForward, + faGear, faGift, faGlobe, faHammer, @@ -58,6 +61,7 @@ import { faHouseChimney, faImage, faImagePortrait, + faInfo, faLeftRight, faLightbulb, faLink, @@ -72,8 +76,8 @@ import { faPersonCircleCheck, faPersonCircleQuestion, faPlus, - faQuestion, faQrcode, + faQuestion, faRightFromBracket, faRotate, faShareNodes, @@ -106,6 +110,7 @@ library.add( faCameraRotate, faCaretDown, faChair, + faChartLine, faCheck, faChevronDown, faChevronLeft, @@ -114,6 +119,7 @@ library.add( faCircle, faCircleCheck, faCircleInfo, + faCirclePlus, faCircleQuestion, faCircleRight, faCircleUser, @@ -135,6 +141,7 @@ library.add( faFloppyDisk, faFolderOpen, faForward, + faGear, faGift, faGlobe, faHammer, @@ -144,6 +151,7 @@ library.add( faHouseChimney, faImage, faImagePortrait, + faInfo, faLeftRight, faLightbulb, faLink, diff --git a/src/libs/util.ts b/src/libs/util.ts index 4fb0aa93..c64916cc 100644 --- a/src/libs/util.ts +++ b/src/libs/util.ts @@ -33,6 +33,7 @@ import { logger } from "../utils/logger"; import { PlatformServiceFactory } from "../services/PlatformServiceFactory"; import { IIdentifier } from "@veramo/core"; import { DEFAULT_ROOT_DERIVATION_PATH } from "./crypto"; +import { UNNAMED_PERSON } from "@/constants/entities"; // Consolidate this with src/utils/PlatformServiceMixin.mapQueryResultToValues function mapQueryResultToValues( @@ -227,7 +228,7 @@ export const nameForContact = ( ): string => { return ( (contact?.name as string) || - (capitalize ? "This" : "this") + " unnamed user" + (capitalize ? "This" : "this") + " " + UNNAMED_PERSON ); }; @@ -648,57 +649,64 @@ export const retrieveAllAccountsMetadata = async (): Promise< return result; }; +export const DUPLICATE_ACCOUNT_ERROR = "Cannot import duplicate account."; + /** - * Saves a new identity to both SQL and Dexie databases + * Saves a new identity to SQL database */ export async function saveNewIdentity( identity: IIdentifier, mnemonic: string, derivationPath: string, ): Promise<void> { - try { - // add to the new sql db - const platformService = await getPlatformService(); - - const secrets = await platformService.dbQuery( - `SELECT secretBase64 FROM secret`, - ); - if (!secrets?.values?.length || !secrets.values[0]?.length) { - throw new Error( - "No initial encryption supported. We recommend you clear your data and start over.", - ); - } + // add to the new sql db + const platformService = await getPlatformService(); - const secretBase64 = secrets.values[0][0] as string; + // Check if account already exists before attempting to save + const existingAccount = await platformService.dbQuery( + "SELECT did FROM accounts WHERE did = ?", + [identity.did], + ); - const secret = base64ToArrayBuffer(secretBase64); - const identityStr = JSON.stringify(identity); - const encryptedIdentity = await simpleEncrypt(identityStr, secret); - const encryptedMnemonic = await simpleEncrypt(mnemonic, secret); - const encryptedIdentityBase64 = arrayBufferToBase64(encryptedIdentity); - const encryptedMnemonicBase64 = arrayBufferToBase64(encryptedMnemonic); + if (existingAccount?.values?.length) { + throw new Error( + `Account with DID ${identity.did} already exists. ${DUPLICATE_ACCOUNT_ERROR}`, + ); + } - const sql = `INSERT INTO accounts (dateCreated, derivationPath, did, identityEncrBase64, mnemonicEncrBase64, publicKeyHex) - VALUES (?, ?, ?, ?, ?, ?)`; - const params = [ - new Date().toISOString(), - derivationPath, - identity.did, - encryptedIdentityBase64, - encryptedMnemonicBase64, - identity.keys[0].publicKeyHex, - ]; - await platformService.dbExec(sql, params); - - await platformService.updateDefaultSettings({ activeDid: identity.did }); - - await platformService.insertDidSpecificSettings(identity.did); - } catch (error) { - logger.error("Failed to update default settings:", error); + const secrets = await platformService.dbQuery( + `SELECT secretBase64 FROM secret`, + ); + if (!secrets?.values?.length || !secrets.values[0]?.length) { throw new Error( - "Failed to set default settings. Please try again or restart the app.", + "No initial encryption supported. We recommend you clear your data and start over.", ); } + + const secretBase64 = secrets.values[0][0] as string; + + const secret = base64ToArrayBuffer(secretBase64); + const identityStr = JSON.stringify(identity); + const encryptedIdentity = await simpleEncrypt(identityStr, secret); + const encryptedMnemonic = await simpleEncrypt(mnemonic, secret); + const encryptedIdentityBase64 = arrayBufferToBase64(encryptedIdentity); + const encryptedMnemonicBase64 = arrayBufferToBase64(encryptedMnemonic); + + const sql = `INSERT INTO accounts (dateCreated, derivationPath, did, identityEncrBase64, mnemonicEncrBase64, publicKeyHex) + VALUES (?, ?, ?, ?, ?, ?)`; + const params = [ + new Date().toISOString(), + derivationPath, + identity.did, + encryptedIdentityBase64, + encryptedMnemonicBase64, + identity.keys[0].publicKeyHex, + ]; + await platformService.dbExec(sql, params); + + await platformService.updateDefaultSettings({ activeDid: identity.did }); + + await platformService.insertNewDidIntoSettings(identity.did); } /** @@ -989,7 +997,7 @@ export async function importFromMnemonic( try { // First, ensure the DID-specific settings record exists - await platformService.insertDidSpecificSettings(newId.did); + await platformService.insertNewDidIntoSettings(newId.did); // Then update with Test User #0 specific settings await platformService.updateDidSpecificSettings(newId.did, { @@ -1008,13 +1016,16 @@ export async function importFromMnemonic( const firstName = settings[0]; const isRegistered = settings[1]; - logger.info("[importFromMnemonic] Test User #0 settings verification", { - did: newId.did, - firstName, - isRegistered, - expectedFirstName: "User Zero", - expectedIsRegistered: true, - }); + logger.debug( + "[importFromMnemonic] Test User #0 settings verification", + { + did: newId.did, + firstName, + isRegistered, + expectedFirstName: "User Zero", + expectedIsRegistered: true, + }, + ); // If settings weren't saved correctly, try individual updates if (firstName !== "User Zero" || isRegistered !== 1) { @@ -1040,7 +1051,7 @@ export async function importFromMnemonic( if (retryResult?.values?.length) { const retrySettings = retryResult.values[0]; - logger.info( + logger.debug( "[importFromMnemonic] Test User #0 settings after retry", { firstName: retrySettings[0], @@ -1063,3 +1074,58 @@ export async function importFromMnemonic( } } } + +/** + * Checks if an account with the given DID already exists in the database + * + * @param did - The DID to check for duplicates + * @returns Promise<boolean> - True if account already exists, false otherwise + * @throws Error if database query fails + */ +export async function checkForDuplicateAccount(did: string): Promise<boolean>; + +/** + * Checks if an account with the given DID already exists in the database + * + * @param mnemonic - The mnemonic phrase to derive DID from + * @param derivationPath - The derivation path to use + * @returns Promise<boolean> - True if account already exists, false otherwise + * @throws Error if database query fails + */ +export async function checkForDuplicateAccount( + mnemonic: string, + derivationPath: string, +): Promise<boolean>; + +/** + * Implementation of checkForDuplicateAccount with overloaded signatures + */ +export async function checkForDuplicateAccount( + didOrMnemonic: string, + derivationPath?: string, +): Promise<boolean> { + let didToCheck: string; + + if (derivationPath) { + // Derive the DID from mnemonic and derivation path + const [address, privateHex, publicHex] = deriveAddress( + didOrMnemonic.trim().toLowerCase(), + derivationPath, + ); + + const newId = newIdentifier(address, privateHex, publicHex, derivationPath); + didToCheck = newId.did; + } else { + // Use the provided DID directly + didToCheck = didOrMnemonic; + } + + // Check if an account with this DID already exists + const platformService = await getPlatformService(); + const existingAccount = await platformService.dbQuery( + "SELECT did FROM accounts WHERE did = ?", + [didToCheck], + ); + + return (existingAccount?.values?.length ?? 0) > 0; +} diff --git a/src/main.capacitor.ts b/src/main.capacitor.ts index 191d356e..19cbf4e7 100644 --- a/src/main.capacitor.ts +++ b/src/main.capacitor.ts @@ -29,14 +29,15 @@ */ import { initializeApp } from "./main.common"; -import { App } from "./libs/capacitor/app"; +import { App as CapacitorApp } from "@capacitor/app"; import router from "./router"; import { handleApiError } from "./services/api"; import { AxiosError } from "axios"; import { DeepLinkHandler } from "./services/deepLinks"; import { logger, safeStringify } from "./utils/logger"; +import "./utils/safeAreaInset"; -logger.log("[Capacitor] Starting initialization"); +logger.log("[Capacitor] 🚀 Starting initialization"); logger.log("[Capacitor] Platform:", process.env.VITE_PLATFORM); const app = initializeApp(); @@ -67,23 +68,123 @@ const deepLinkHandler = new DeepLinkHandler(router); * @throws {Error} If URL format is invalid */ const handleDeepLink = async (data: { url: string }) => { + const { url } = data; + logger.info(`[Main] 🌐 Deeplink received from Capacitor: ${url}`); + try { + // Wait for router to be ready + logger.info(`[Main] ⏳ Waiting for router to be ready...`); await router.isReady(); - await deepLinkHandler.handleDeepLink(data.url); + logger.info(`[Main] ✅ Router is ready, processing deeplink`); + + // Process the deeplink + logger.info(`[Main] 🚀 Starting deeplink processing`); + await deepLinkHandler.handleDeepLink(url); + logger.info(`[Main] ✅ Deeplink processed successfully`); } catch (error) { - logger.error("[DeepLink] Error handling deep link: ", error); + logger.error(`[Main] ❌ Deeplink processing failed:`, { + url, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + timestamp: new Date().toISOString(), + }); + + // Log additional context for debugging + logger.error(`[Main] 🔍 Debug context:`, { + routerReady: router.isReady(), + currentRoute: router.currentRoute.value, + appMounted: app._instance?.isMounted, + timestamp: new Date().toISOString(), + }); + + // Fallback to original error handling let message: string = error instanceof Error ? error.message : safeStringify(error); - if (data.url) { - message += `\nURL: ${data.url}`; + if (url) { + message += `\nURL: ${url}`; } handleApiError({ message } as AxiosError, "deep-link"); } }; -// Register deep link handler with Capacitor -App.addListener("appUrlOpen", handleDeepLink); +// Function to register the deeplink listener +const registerDeepLinkListener = async () => { + try { + logger.info( + `[Main] 🔗 Attempting to register deeplink handler with Capacitor`, + ); + + // Check if Capacitor App plugin is available + logger.info(`[Main] 🔍 Checking Capacitor App plugin availability...`); + if (!CapacitorApp) { + throw new Error("Capacitor App plugin not available"); + } + logger.info(`[Main] ✅ Capacitor App plugin is available`); + + // Check available methods on CapacitorApp + logger.info( + `[Main] 🔍 Capacitor App plugin methods:`, + Object.getOwnPropertyNames(CapacitorApp), + ); + logger.info( + `[Main] 🔍 Capacitor App plugin addListener method:`, + typeof CapacitorApp.addListener, + ); + + // Wait for router to be ready first + await router.isReady(); + logger.info( + `[Main] ✅ Router is ready, proceeding with listener registration`, + ); + + // Try to register the listener + logger.info(`[Main] 🧪 Attempting to register appUrlOpen listener...`); + const listenerHandle = await CapacitorApp.addListener( + "appUrlOpen", + handleDeepLink, + ); + logger.info( + `[Main] ✅ appUrlOpen listener registered successfully with handle:`, + listenerHandle, + ); -logger.log("[Capacitor] Mounting app"); + // Test the listener registration by checking if it's actually registered + logger.info(`[Main] 🧪 Verifying listener registration...`); + + return listenerHandle; + } catch (error) { + logger.error(`[Main] ❌ Failed to register deeplink listener:`, { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + timestamp: new Date().toISOString(), + }); + throw error; + } +}; + +logger.log("[Capacitor] 🚀 Mounting app"); app.mount("#app"); -logger.log("[Capacitor] App mounted"); +logger.info(`[Main] ✅ App mounted successfully`); + +// Register deeplink listener after app is mounted +setTimeout(async () => { + try { + logger.info( + `[Main] ⏳ Delaying listener registration to ensure Capacitor is ready...`, + ); + await registerDeepLinkListener(); + logger.info(`[Main] 🎉 Deep link system fully initialized!`); + } catch (error) { + logger.error(`[Main] ❌ Deep link system initialization failed:`, error); + } +}, 2000); // 2 second delay to ensure Capacitor is fully ready + +// Log app initialization status +setTimeout(() => { + logger.info(`[Main] 📊 App initialization status:`, { + routerReady: router.isReady(), + currentRoute: router.currentRoute.value, + appMounted: app._instance?.isMounted, + timestamp: new Date().toISOString(), + }); +}, 1000); diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 00000000..cc05e386 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,35 @@ +/** + * @file Dynamic Main Entry Point + * @author Matthew Raymer + * + * This file dynamically loads the appropriate platform-specific main entry point + * based on the current environment and build configuration. + */ + +import { logger } from "./utils/logger"; + +// Check the platform from environment variables +const platform = process.env.VITE_PLATFORM || "web"; + +logger.info(`[Main] 🚀 Loading TimeSafari for platform: ${platform}`); + +// Log all relevant environment variables for boot-time debugging +logger.info("[Main] 🌍 Boot-time environment configuration:", { + platform: process.env.VITE_PLATFORM, + defaultEndorserApiServer: process.env.VITE_DEFAULT_ENDORSER_API_SERVER, + defaultPartnerApiServer: process.env.VITE_DEFAULT_PARTNER_API_SERVER, + nodeEnv: process.env.NODE_ENV, + timestamp: new Date().toISOString(), +}); + +// Dynamically import the appropriate main entry point +if (platform === "capacitor") { + logger.info(`[Main] 📱 Loading Capacitor-specific entry point`); + import("./main.capacitor"); +} else if (platform === "electron") { + logger.info(`[Main] 💻 Loading Electron-specific entry point`); + import("./main.electron"); +} else { + logger.info(`[Main] 🌐 Loading Web-specific entry point`); + import("./main.web"); +} diff --git a/src/router/index.ts b/src/router/index.ts index 043d3d0c..cf450c37 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -321,25 +321,38 @@ const errorHandler = ( router.onError(errorHandler); // Assign the error handler to the router instance /** - * Global navigation guard to ensure user identity exists - * - * This guard checks if the user has any identities before navigating to most routes. - * If no identity exists, it automatically creates one using the default seed-based method. - * - * Routes that are excluded from this check: - * - /start - Manual identity creation selection - * - /new-identifier - Manual seed-based creation - * - /import-account - Manual import flow - * - /import-derive - Manual derivation flow - * - /database-migration - Migration utilities - * - /deep-link-error - Error page - * + * Navigation guard to ensure user has an identity before accessing protected routes * @param to - Target route - * @param from - Source route + * @param _from - Source route (unused) * @param next - Navigation function */ router.beforeEach(async (to, _from, next) => { + logger.debug(`[Router] 🧭 Navigation guard triggered:`, { + from: _from?.path || "none", + to: to.path, + name: to.name, + params: to.params, + query: to.query, + timestamp: new Date().toISOString(), + }); + try { + // Log boot-time configuration on first navigation + if (!_from) { + logger.info( + "[Router] 🚀 First navigation detected - logging boot-time configuration:", + { + platform: process.env.VITE_PLATFORM, + defaultEndorserApiServer: + process.env.VITE_DEFAULT_ENDORSER_API_SERVER, + defaultPartnerApiServer: process.env.VITE_DEFAULT_PARTNER_API_SERVER, + nodeEnv: process.env.NODE_ENV, + targetRoute: to.path, + timestamp: new Date().toISOString(), + }, + ); + } + // Skip identity check for routes that handle identity creation manually const skipIdentityRoutes = [ "/start", @@ -351,32 +364,67 @@ router.beforeEach(async (to, _from, next) => { ]; if (skipIdentityRoutes.includes(to.path)) { + logger.debug(`[Router] ⏭️ Skipping identity check for route: ${to.path}`); return next(); } + logger.debug(`[Router] 🔍 Checking user identity for route: ${to.path}`); + // Check if user has any identities const allMyDids = await retrieveAccountDids(); + logger.debug(`[Router] 📋 Found ${allMyDids.length} user identities`); if (allMyDids.length === 0) { - logger.info("[Router] No identities found, creating default identity"); + logger.info("[Router] ⚠️ No identities found, creating default identity"); // Create identity automatically using seed-based method await generateSaveAndActivateIdentity(); - logger.info("[Router] Default identity created successfully"); + logger.info("[Router] ✅ Default identity created successfully"); + } else { + logger.debug( + `[Router] ✅ User has ${allMyDids.length} identities, proceeding`, + ); } + logger.info(`[Router] ✅ Navigation guard passed for: ${to.path}`); next(); } catch (error) { - logger.error( - "[Router] Identity creation failed in navigation guard:", - error, - ); + logger.error("[Router] ❌ Identity creation failed in navigation guard:", { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + route: to.path, + timestamp: new Date().toISOString(), + }); // Redirect to start page if identity creation fails // This allows users to manually create an identity or troubleshoot + logger.info( + `[Router] 🔄 Redirecting to /start due to identity creation failure`, + ); next("/start"); } }); +// Add navigation success logging +router.afterEach((to, from) => { + logger.debug(`[Router] ✅ Navigation completed:`, { + from: from?.path || "none", + to: to.path, + name: to.name, + params: to.params, + query: to.query, + timestamp: new Date().toISOString(), + }); +}); + +// Add error logging +router.onError((error) => { + logger.error(`[Router] ❌ Navigation error:`, { + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, + timestamp: new Date().toISOString(), + }); +}); + export default router; diff --git a/src/services/ClipboardService.ts b/src/services/ClipboardService.ts new file mode 100644 index 00000000..0e68f95b --- /dev/null +++ b/src/services/ClipboardService.ts @@ -0,0 +1,185 @@ +import { Capacitor } from "@capacitor/core"; +import { Clipboard } from "@capacitor/clipboard"; +import { useClipboard } from "@vueuse/core"; +import { logger } from "@/utils/logger"; + +/** + * Platform-agnostic clipboard service that handles both web and native platforms + * Provides reliable clipboard functionality across all platforms including iOS + */ +export class ClipboardService { + private static instance: ClipboardService | null = null; + + /** + * Get singleton instance of ClipboardService + */ + public static getInstance(): ClipboardService { + if (!ClipboardService.instance) { + ClipboardService.instance = new ClipboardService(); + } + return ClipboardService.instance; + } + + /** + * Copy text to clipboard with platform-specific handling + * + * @param text - The text to copy to clipboard + * @returns Promise that resolves when copy is complete + * @throws Error if copy operation fails + */ + public async copyToClipboard(text: string): Promise<void> { + const platform = Capacitor.getPlatform(); + const isNative = Capacitor.isNativePlatform(); + + logger.debug("[ClipboardService] Copying to clipboard:", { + text: text.substring(0, 50) + (text.length > 50 ? "..." : ""), + platform, + isNative, + timestamp: new Date().toISOString(), + }); + + try { + if (isNative && (platform === "ios" || platform === "android")) { + // Use native Capacitor clipboard for mobile platforms + await this.copyNative(text); + } else { + // Use web clipboard API for web/desktop platforms + await this.copyWeb(text); + } + + logger.debug("[ClipboardService] Copy successful", { + platform, + timestamp: new Date().toISOString(), + }); + } catch (error) { + logger.error("[ClipboardService] Copy failed:", { + error: error instanceof Error ? error.message : String(error), + platform, + timestamp: new Date().toISOString(), + }); + throw error; + } + } + + /** + * Copy text using native Capacitor clipboard API + * + * @param text - The text to copy + * @returns Promise that resolves when copy is complete + */ + private async copyNative(text: string): Promise<void> { + try { + await Clipboard.write({ + string: text, + }); + } catch (error) { + logger.error("[ClipboardService] Native copy failed:", { + error: error instanceof Error ? error.message : String(error), + timestamp: new Date().toISOString(), + }); + throw new Error( + `Native clipboard copy failed: ${error instanceof Error ? error.message : String(error)}`, + ); + } + } + + /** + * Copy text using web clipboard API with fallback + * + * @param text - The text to copy + * @returns Promise that resolves when copy is complete + */ + private async copyWeb(text: string): Promise<void> { + try { + // Try VueUse clipboard first (handles some edge cases) + const { copy } = useClipboard(); + await copy(text); + } catch (error) { + logger.warn( + "[ClipboardService] VueUse clipboard failed, trying native API:", + { + error: error instanceof Error ? error.message : String(error), + timestamp: new Date().toISOString(), + }, + ); + + // Fallback to native navigator.clipboard + if (navigator.clipboard && navigator.clipboard.writeText) { + await navigator.clipboard.writeText(text); + } else { + throw new Error("Clipboard API not supported in this browser"); + } + } + } + + /** + * Read text from clipboard (platform-specific) + * + * @returns Promise that resolves to the clipboard text + * @throws Error if read operation fails + */ + public async readFromClipboard(): Promise<string> { + const platform = Capacitor.getPlatform(); + const isNative = Capacitor.isNativePlatform(); + + try { + if (isNative && (platform === "ios" || platform === "android")) { + // Use native Capacitor clipboard for mobile platforms + const result = await Clipboard.read(); + return result.value || ""; + } else { + // Use web clipboard API for web/desktop platforms + if (navigator.clipboard && navigator.clipboard.readText) { + return await navigator.clipboard.readText(); + } else { + throw new Error("Clipboard read API not supported in this browser"); + } + } + } catch (error) { + logger.error("[ClipboardService] Read from clipboard failed:", { + error: error instanceof Error ? error.message : String(error), + platform, + timestamp: new Date().toISOString(), + }); + throw error; + } + } + + /** + * Check if clipboard is supported on current platform + * + * @returns boolean indicating if clipboard is supported + */ + public isSupported(): boolean { + const platform = Capacitor.getPlatform(); + const isNative = Capacitor.isNativePlatform(); + + if (isNative && (platform === "ios" || platform === "android")) { + return true; // Capacitor clipboard should work on native platforms + } + + // Check web clipboard support + return !!(navigator.clipboard && navigator.clipboard.writeText); + } +} + +/** + * Convenience function to copy text to clipboard + * Uses the singleton ClipboardService instance + * + * @param text - The text to copy to clipboard + * @returns Promise that resolves when copy is complete + */ +export async function copyToClipboard(text: string): Promise<void> { + return ClipboardService.getInstance().copyToClipboard(text); +} + +/** + * Convenience function to read text from clipboard + * Uses the singleton ClipboardService instance + * + * @returns Promise that resolves to the clipboard text + */ +export async function readFromClipboard(): Promise<string> { + return ClipboardService.getInstance().readFromClipboard(); +} diff --git a/src/services/PlatformService.ts b/src/services/PlatformService.ts index a3dbff6a..ede6a5b0 100644 --- a/src/services/PlatformService.ts +++ b/src/services/PlatformService.ts @@ -175,11 +175,11 @@ export interface PlatformService { updateDefaultSettings(settings: Record<string, unknown>): Promise<void>; /** - * Inserts DID-specific settings into the database. + * Inserts a new DID into the settings table. * @param did - The DID to associate with the settings * @returns Promise that resolves when the insertion is complete */ - insertDidSpecificSettings(did: string): Promise<void>; + insertNewDidIntoSettings(did: string): Promise<void>; /** * Updates DID-specific settings in the database. diff --git a/src/services/ProfileService.ts b/src/services/ProfileService.ts deleted file mode 100644 index eb1f347b..00000000 --- a/src/services/ProfileService.ts +++ /dev/null @@ -1,224 +0,0 @@ -/** - * ProfileService - Handles user profile operations and API calls - * Extracted from AccountViewView.vue to improve separation of concerns - */ - -import { AxiosInstance, AxiosError } from "axios"; -import { UserProfile } from "@/libs/partnerServer"; -import { UserProfileResponse } from "@/interfaces/accountView"; -import { getHeaders, errorStringForLog } from "@/libs/endorserServer"; -import { handleApiError } from "./api"; -import { logger } from "@/utils/logger"; -import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView"; - -/** - * Profile data interface - */ -export interface ProfileData { - description: string; - latitude: number; - longitude: number; - includeLocation: boolean; -} - -/** - * Profile service class - */ -export class ProfileService { - private axios: AxiosInstance; - private partnerApiServer: string; - - constructor(axios: AxiosInstance, partnerApiServer: string) { - this.axios = axios; - this.partnerApiServer = partnerApiServer; - } - - /** - * Load user profile from the server - * @param activeDid - The user's DID - * @returns ProfileData or null if profile doesn't exist - */ - async loadProfile(activeDid: string): Promise<ProfileData | null> { - try { - const headers = await getHeaders(activeDid); - const response = await this.axios.get<UserProfileResponse>( - `${this.partnerApiServer}/api/partner/userProfileForIssuer/${activeDid}`, - { headers }, - ); - - if (response.status === 200) { - const data = response.data.data; - const profileData: ProfileData = { - description: data.description || "", - latitude: data.locLat || 0, - longitude: data.locLon || 0, - includeLocation: !!(data.locLat && data.locLon), - }; - return profileData; - } else { - throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.UNABLE_TO_LOAD_PROFILE); - } - } catch (error) { - if (this.isApiError(error) && error.response?.status === 404) { - // Profile doesn't exist yet - this is normal - return null; - } - - logger.error("Error loading profile:", errorStringForLog(error)); - handleApiError(error as AxiosError, "/api/partner/userProfileForIssuer"); - return null; - } - } - - /** - * Save user profile to the server - * @param activeDid - The user's DID - * @param profileData - The profile data to save - * @returns true if successful, false otherwise - */ - async saveProfile( - activeDid: string, - profileData: ProfileData, - ): Promise<boolean> { - try { - const headers = await getHeaders(activeDid); - const payload: UserProfile = { - description: profileData.description, - issuerDid: activeDid, - }; - - // Add location data if location is included - if ( - profileData.includeLocation && - profileData.latitude && - profileData.longitude - ) { - payload.locLat = profileData.latitude; - payload.locLon = profileData.longitude; - } - - const response = await this.axios.post( - `${this.partnerApiServer}/api/partner/userProfile`, - payload, - { headers }, - ); - - if (response.status === 201) { - return true; - } else { - logger.error("Error saving profile:", response); - throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_SAVED); - } - } catch (error) { - logger.error("Error saving profile:", errorStringForLog(error)); - handleApiError(error as AxiosError, "/api/partner/userProfile"); - return false; - } - } - - /** - * Delete user profile from the server - * @param activeDid - The user's DID - * @returns true if successful, false otherwise - */ - async deleteProfile(activeDid: string): Promise<boolean> { - try { - const headers = await getHeaders(activeDid); - const response = await this.axios.delete( - `${this.partnerApiServer}/api/partner/userProfile`, - { headers }, - ); - - if (response.status === 200) { - return true; - } else { - throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_DELETED); - } - } catch (error) { - logger.error("Error deleting profile:", errorStringForLog(error)); - handleApiError(error as AxiosError, "/api/partner/userProfile"); - return false; - } - } - - /** - * Update profile location - * @param profileData - Current profile data - * @param latitude - New latitude - * @param longitude - New longitude - * @returns Updated profile data - */ - updateProfileLocation( - profileData: ProfileData, - latitude: number, - longitude: number, - ): ProfileData { - return { - ...profileData, - latitude, - longitude, - includeLocation: true, - }; - } - - /** - * Toggle location inclusion in profile - * @param profileData - Current profile data - * @returns Updated profile data - */ - toggleProfileLocation(profileData: ProfileData): ProfileData { - const includeLocation = !profileData.includeLocation; - return { - ...profileData, - latitude: includeLocation ? profileData.latitude : 0, - longitude: includeLocation ? profileData.longitude : 0, - includeLocation, - }; - } - - /** - * Clear profile location - * @param profileData - Current profile data - * @returns Updated profile data - */ - clearProfileLocation(profileData: ProfileData): ProfileData { - return { - ...profileData, - latitude: 0, - longitude: 0, - includeLocation: false, - }; - } - - /** - * Reset profile to default state - * @returns Default profile data - */ - getDefaultProfile(): ProfileData { - return { - description: "", - latitude: 0, - longitude: 0, - includeLocation: false, - }; - } - - /** - * Type guard for API errors - */ - private isApiError( - error: unknown, - ): error is { response?: { status?: number } } { - return typeof error === "object" && error !== null && "response" in error; - } -} - -/** - * Factory function to create a ProfileService instance - */ -export function createProfileService( - axios: AxiosInstance, - partnerApiServer: string, -): ProfileService { - return new ProfileService(axios, partnerApiServer); -} diff --git a/src/services/QRNavigationService.ts b/src/services/QRNavigationService.ts new file mode 100644 index 00000000..33716c39 --- /dev/null +++ b/src/services/QRNavigationService.ts @@ -0,0 +1,99 @@ +import { PlatformServiceFactory } from "./PlatformServiceFactory"; +import { PlatformService } from "./PlatformService"; +import { logger } from "@/utils/logger"; + +/** + * QR Navigation Service + * + * Handles platform-specific routing logic for QR scanning operations. + * Removes coupling between views and routing logic by centralizing + * navigation decisions based on platform capabilities. + * + * @author Matthew Raymer + */ +export class QRNavigationService { + private static instance: QRNavigationService | null = null; + private platformService: PlatformService; + + private constructor() { + this.platformService = PlatformServiceFactory.getInstance(); + } + + /** + * Get singleton instance of QRNavigationService + */ + public static getInstance(): QRNavigationService { + if (!QRNavigationService.instance) { + QRNavigationService.instance = new QRNavigationService(); + } + return QRNavigationService.instance; + } + + /** + * Get the appropriate QR scanner route based on platform + * + * @returns Object with route name and parameters for QR scanning + */ + public getQRScannerRoute(): { + name: string; + params?: Record<string, string | number>; + } { + const isCapacitor = this.platformService.isCapacitor(); + + logger.debug("QR Navigation - Platform detection:", { + isCapacitor, + platform: this.platformService.getCapabilities(), + }); + + if (isCapacitor) { + // Use native scanner on mobile platforms + return { name: "contact-qr-scan-full" }; + } else { + // Use web scanner on other platforms + return { name: "contact-qr" }; + } + } + + /** + * Get the appropriate QR display route based on platform + * + * @returns Object with route name and parameters for QR display + */ + public getQRDisplayRoute(): { + name: string; + params?: Record<string, string | number>; + } { + const isCapacitor = this.platformService.isCapacitor(); + + logger.debug("QR Navigation - Display route detection:", { + isCapacitor, + platform: this.platformService.getCapabilities(), + }); + + if (isCapacitor) { + // Use dedicated display view on mobile + return { name: "contact-qr-scan-show" }; + } else { + // Use combined view on web + return { name: "contact-qr" }; + } + } + + /** + * Check if native QR scanning is available on current platform + * + * @returns true if native scanning is available, false otherwise + */ + public isNativeScanningAvailable(): boolean { + return this.platformService.isCapacitor(); + } + + /** + * Get platform capabilities for QR operations + * + * @returns Platform capabilities object + */ + public getPlatformCapabilities() { + return this.platformService.getCapabilities(); + } +} diff --git a/src/services/QRScanner/WebInlineQRScanner.ts b/src/services/QRScanner/WebInlineQRScanner.ts index 5f5bceaf..d48775fe 100644 --- a/src/services/QRScanner/WebInlineQRScanner.ts +++ b/src/services/QRScanner/WebInlineQRScanner.ts @@ -36,7 +36,7 @@ export class WebInlineQRScanner implements QRScannerService { // Generate a short random ID for this scanner instance this.id = Math.random().toString(36).substring(2, 8).toUpperCase(); this.options = options ?? {}; - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Initializing scanner with options:`, { ...this.options, @@ -49,7 +49,7 @@ export class WebInlineQRScanner implements QRScannerService { this.context = this.canvas.getContext("2d", { willReadFrequently: true }); this.video = document.createElement("video"); this.video.setAttribute("playsinline", "true"); // Required for iOS - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] DOM elements created successfully`, ); } @@ -60,7 +60,7 @@ export class WebInlineQRScanner implements QRScannerService { this.cameraStateListeners.forEach((listener) => { try { listener.onStateChange(state, message); - logger.info( + logger.debug( `[WebInlineQRScanner:${this.id}] Camera state changed to: ${state}`, { state, @@ -89,7 +89,7 @@ export class WebInlineQRScanner implements QRScannerService { async checkPermissions(): Promise<boolean> { try { this.updateCameraState("initializing", "Checking camera permissions..."); - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Checking camera permissions...`, ); @@ -99,7 +99,7 @@ export class WebInlineQRScanner implements QRScannerService { const permissions = await navigator.permissions.query({ name: "camera" as PermissionName, }); - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Permission state from Permissions API:`, permissions.state, ); @@ -165,7 +165,7 @@ export class WebInlineQRScanner implements QRScannerService { "initializing", "Requesting camera permissions...", ); - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Requesting camera permissions...`, ); @@ -175,7 +175,7 @@ export class WebInlineQRScanner implements QRScannerService { (device) => device.kind === "videoinput", ); - logger.error(`[WebInlineQRScanner:${this.id}] Found video devices:`, { + logger.debug(`[WebInlineQRScanner:${this.id}] Found video devices:`, { count: videoDevices.length, devices: videoDevices.map((d) => ({ id: d.deviceId, label: d.label })), userAgent: navigator.userAgent, @@ -188,7 +188,7 @@ export class WebInlineQRScanner implements QRScannerService { } // Try to get a stream with specific constraints - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Requesting camera stream with constraints:`, { facingMode: "environment", @@ -210,7 +210,7 @@ export class WebInlineQRScanner implements QRScannerService { // Stop the test stream immediately stream.getTracks().forEach((track) => { - logger.error(`[WebInlineQRScanner:${this.id}] Stopping test track:`, { + logger.debug(`[WebInlineQRScanner:${this.id}] Stopping test track:`, { kind: track.kind, label: track.label, readyState: track.readyState, @@ -275,12 +275,12 @@ export class WebInlineQRScanner implements QRScannerService { async isSupported(): Promise<boolean> { try { - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Checking browser support...`, ); // Check for secure context first if (!window.isSecureContext) { - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Camera access requires HTTPS (secure context)`, ); return false; @@ -300,7 +300,7 @@ export class WebInlineQRScanner implements QRScannerService { (device) => device.kind === "videoinput", ); - logger.error(`[WebInlineQRScanner:${this.id}] Device support check:`, { + logger.debug(`[WebInlineQRScanner:${this.id}] Device support check:`, { hasSecureContext: window.isSecureContext, hasMediaDevices: !!navigator.mediaDevices, hasGetUserMedia: !!navigator.mediaDevices?.getUserMedia, @@ -379,7 +379,7 @@ export class WebInlineQRScanner implements QRScannerService { // Log scan attempt every 100 frames or 1 second if (this.scanAttempts % 100 === 0 || timeSinceLastScan >= 1000) { - logger.error(`[WebInlineQRScanner:${this.id}] Scanning frame:`, { + logger.debug(`[WebInlineQRScanner:${this.id}] Scanning frame:`, { attempt: this.scanAttempts, dimensions: { width: this.canvas.width, @@ -421,7 +421,7 @@ export class WebInlineQRScanner implements QRScannerService { !code.data || code.data.length === 0; - logger.error(`[WebInlineQRScanner:${this.id}] QR Code detected:`, { + logger.debug(`[WebInlineQRScanner:${this.id}] QR Code detected:`, { data: code.data, location: code.location, attempts: this.scanAttempts, @@ -512,13 +512,13 @@ export class WebInlineQRScanner implements QRScannerService { this.scanAttempts = 0; this.lastScanTime = Date.now(); this.updateCameraState("initializing", "Starting camera..."); - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Starting scan with options:`, this.options, ); // Get camera stream with options - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Requesting camera stream...`, ); this.stream = await navigator.mediaDevices.getUserMedia({ @@ -531,7 +531,7 @@ export class WebInlineQRScanner implements QRScannerService { this.updateCameraState("active", "Camera is active"); - logger.error(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, { + logger.debug(`[WebInlineQRScanner:${this.id}] Camera stream obtained:`, { tracks: this.stream.getTracks().map((t) => ({ kind: t.kind, label: t.label, @@ -550,14 +550,14 @@ export class WebInlineQRScanner implements QRScannerService { this.video.style.display = "none"; } await this.video.play(); - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Video element started playing`, ); } // Emit stream to component this.events.emit("stream", this.stream); - logger.error(`[WebInlineQRScanner:${this.id}] Stream event emitted`); + logger.debug(`[WebInlineQRScanner:${this.id}] Stream event emitted`); // Start QR code scanning this.scanQRCode(); @@ -595,7 +595,7 @@ export class WebInlineQRScanner implements QRScannerService { } try { - logger.error(`[WebInlineQRScanner:${this.id}] Stopping scan`, { + logger.debug(`[WebInlineQRScanner:${this.id}] Stopping scan`, { scanAttempts: this.scanAttempts, duration: Date.now() - this.lastScanTime, }); @@ -604,7 +604,7 @@ export class WebInlineQRScanner implements QRScannerService { if (this.animationFrameId !== null) { cancelAnimationFrame(this.animationFrameId); this.animationFrameId = null; - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Animation frame cancelled`, ); } @@ -613,13 +613,13 @@ export class WebInlineQRScanner implements QRScannerService { if (this.video) { this.video.pause(); this.video.srcObject = null; - logger.error(`[WebInlineQRScanner:${this.id}] Video element stopped`); + logger.debug(`[WebInlineQRScanner:${this.id}] Video element stopped`); } // Stop all tracks in the stream if (this.stream) { this.stream.getTracks().forEach((track) => { - logger.error(`[WebInlineQRScanner:${this.id}] Stopping track:`, { + logger.debug(`[WebInlineQRScanner:${this.id}] Stopping track:`, { kind: track.kind, label: track.label, readyState: track.readyState, @@ -631,7 +631,7 @@ export class WebInlineQRScanner implements QRScannerService { // Emit stream stopped event this.events.emit("stream", null); - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Stream stopped event emitted`, ); } catch (error) { @@ -643,17 +643,17 @@ export class WebInlineQRScanner implements QRScannerService { throw error; } finally { this.isScanning = false; - logger.error(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`); + logger.debug(`[WebInlineQRScanner:${this.id}] Scan stopped successfully`); } } addListener(listener: ScanListener): void { - logger.error(`[WebInlineQRScanner:${this.id}] Adding scan listener`); + logger.debug(`[WebInlineQRScanner:${this.id}] Adding scan listener`); this.scanListener = listener; } onStream(callback: (stream: MediaStream | null) => void): void { - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Adding stream event listener`, ); this.events.on("stream", callback); @@ -661,24 +661,24 @@ export class WebInlineQRScanner implements QRScannerService { async cleanup(): Promise<void> { try { - logger.error(`[WebInlineQRScanner:${this.id}] Starting cleanup`); + logger.debug(`[WebInlineQRScanner:${this.id}] Starting cleanup`); await this.stopScan(); this.events.removeAllListeners(); - logger.error(`[WebInlineQRScanner:${this.id}] Event listeners removed`); + logger.debug(`[WebInlineQRScanner:${this.id}] Event listeners removed`); // Clean up DOM elements if (this.video) { this.video.remove(); this.video = null; - logger.error(`[WebInlineQRScanner:${this.id}] Video element removed`); + logger.debug(`[WebInlineQRScanner:${this.id}] Video element removed`); } if (this.canvas) { this.canvas.remove(); this.canvas = null; - logger.error(`[WebInlineQRScanner:${this.id}] Canvas element removed`); + logger.debug(`[WebInlineQRScanner:${this.id}] Canvas element removed`); } this.context = null; - logger.error( + logger.debug( `[WebInlineQRScanner:${this.id}] Cleanup completed successfully`, ); } catch (error) { diff --git a/src/services/deepLinks.ts b/src/services/deepLinks.ts index d8445607..ec590455 100644 --- a/src/services/deepLinks.ts +++ b/src/services/deepLinks.ts @@ -1,56 +1,22 @@ /** - * @file Deep Link Handler Service - * @author Matthew Raymer - * - * This service handles the processing and routing of deep links in the TimeSafari app. - * It provides a type-safe interface between the raw deep links and the application router. - * - * Architecture: - * 1. DeepLinkHandler class encapsulates all deep link processing logic - * 2. Uses Zod schemas from interfaces/deepLinks for parameter validation - * 3. Provides consistent error handling and logging - * 4. Maps validated parameters to Vue router calls - * - * Error Handling Strategy: - * - All errors are wrapped in DeepLinkError interface - * - Errors include error codes for systematic handling - * - Detailed error information is logged for debugging - * - Errors are propagated to the global error handler - * - * Validation Strategy: - * - URL structure validation - * - Route-specific parameter validation using Zod schemas - * - Query parameter validation and sanitization - * - Type-safe parameter passing to router + * DeepLinks Service * - * Deep Link Format: - * timesafari://<route>[/<param>][?queryParam1=value1&queryParam2=value2] + * Handles deep link processing and routing for the TimeSafari application. + * Supports both path parameters and query parameters with comprehensive validation. * - * Supported Routes: - * - claim: View claim - * - claim-add-raw: Add raw claim - * - claim-cert: View claim certificate - * - confirm-gift - * - contact-import: Import contacts - * - did: View DID - * - invite-one-accept: Accept invitation - * - onboard-meeting-members - * - project: View project details - * - user-profile: View user profile - * - * @example - * const handler = new DeepLinkHandler(router); - * await handler.handleDeepLink("timesafari://claim/123?view=details"); + * @author Matthew Raymer + * @version 2.0.0 + * @since 2025-01-25 */ import { Router } from "vue-router"; import { z } from "zod"; import { - deepLinkSchemas, - baseUrlSchema, + deepLinkPathSchemas, routeSchema, DeepLinkRoute, + deepLinkQuerySchemas, } from "../interfaces/deepLinks"; import type { DeepLinkError } from "../interfaces/deepLinks"; import { logger } from "../utils/logger"; @@ -74,7 +40,7 @@ function getFirstKeyFromZodObject( * because "router.replace" expects the right parameter name for the route. */ export const ROUTE_MAP: Record<string, { name: string; paramKey?: string }> = - Object.entries(deepLinkSchemas).reduce( + Object.entries(deepLinkPathSchemas).reduce( (acc, [routeName, schema]) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const paramKey = getFirstKeyFromZodObject(schema as z.ZodObject<any>); @@ -103,83 +69,152 @@ export class DeepLinkHandler { } /** - - * Parses deep link URL into path, params and query components. - * Validates URL structure using Zod schemas. - * - * @param url - The deep link URL to parse (format: scheme://path[?query]) - * @throws {DeepLinkError} If URL format is invalid - * @returns Parsed URL components (path: string, params: {KEY: string}, query: {KEY: string}) + * Main entry point for processing deep links + * @param url - The deep link URL to process + * @throws {DeepLinkError} If validation fails or route is invalid */ - private parseDeepLink(url: string) { - const parts = url.split("://"); - if (parts.length !== 2) { - throw { code: "INVALID_URL", message: "Invalid URL format" }; - } + async handleDeepLink(url: string): Promise<void> { + logger.debug(`[DeepLink] 🚀 Starting deeplink processing for URL: ${url}`); - // Validate base URL structure - baseUrlSchema.parse({ - scheme: parts[0], - path: parts[1], - queryParams: {}, // Will be populated below - }); + try { + logger.debug(`[DeepLink] 📍 Parsing URL: ${url}`); + const { path, params, query } = this.parseDeepLink(url); - const [path, queryString] = parts[1].split("?"); - const [routePath, ...pathParams] = path.split("/"); + logger.debug(`[DeepLink] ✅ URL parsed successfully:`, { + path, + params: Object.keys(params), + query: Object.keys(query), + fullParams: params, + fullQuery: query, + }); - // Validate route exists before proceeding - if (!ROUTE_MAP[routePath]) { - throw { - code: "INVALID_ROUTE", - message: `Invalid route path: ${routePath}`, - details: { routePath }, - }; - } + // Sanitize parameters (remove undefined values) + const sanitizedParams = Object.fromEntries( + Object.entries(params).map(([key, value]) => [key, value ?? ""]), + ); + + logger.debug(`[DeepLink] 🧹 Parameters sanitized:`, sanitizedParams); - const query: Record<string, string> = {}; - if (queryString) { - new URLSearchParams(queryString).forEach((value, key) => { - query[key] = value; + await this.validateAndRoute(path, sanitizedParams, query); + logger.debug(`[DeepLink] 🎯 Deeplink processing completed successfully`); + } catch (error) { + logger.error(`[DeepLink] ❌ Deeplink processing failed:`, { + url, + error: error instanceof Error ? error.message : String(error), + stack: error instanceof Error ? error.stack : undefined, }); - } - const params: Record<string, string> = {}; - if (pathParams) { - // Now we know routePath exists in ROUTE_MAP - const routeConfig = ROUTE_MAP[routePath]; - params[routeConfig.paramKey ?? "id"] = pathParams.join("/"); + const deepLinkError = error as DeepLinkError; + throw deepLinkError; } + } + + /** + * Parse a deep link URL into its components + * @param url - The deep link URL + * @returns Parsed components + */ + private parseDeepLink(url: string): { + path: string; + params: Record<string, string>; + query: Record<string, string>; + } { + logger.debug(`[DeepLink] 🔍 Parsing deep link: ${url}`); - // logConsoleAndDb( - // `[DeepLink] Debug: Route Path: ${routePath} Path Params: ${JSON.stringify(params)} Query String: ${JSON.stringify(query)}`, - // false, - // ); - return { path: routePath, params, query }; + try { + const parts = url.split("://"); + if (parts.length !== 2) { + throw new Error("Invalid URL format"); + } + + const [path, queryString] = parts[1].split("?"); + const [routePath, ...pathParams] = path.split("/"); + + // Parse path parameters using route-specific configuration + const params: Record<string, string> = {}; + if (pathParams.length > 0) { + // Get the correct parameter key for this route + const routeConfig = ROUTE_MAP[routePath]; + if (routeConfig?.paramKey) { + params[routeConfig.paramKey] = pathParams[0]; + logger.debug( + `[DeepLink] 📍 Path parameter extracted: ${routeConfig.paramKey}=${pathParams[0]}`, + ); + } else { + // Fallback to 'id' for backward compatibility + params.id = pathParams[0]; + logger.debug( + `[DeepLink] 📍 Path parameter extracted: id=${pathParams[0]} (fallback)`, + ); + } + } + + // Parse query parameters + const query: Record<string, string> = {}; + if (queryString) { + const queryParams = new URLSearchParams(queryString); + for (const [key, value] of queryParams.entries()) { + query[key] = value; + } + logger.debug(`[DeepLink] 🔗 Query parameters extracted:`, query); + } + + logger.debug(`[DeepLink] ✅ Parse completed:`, { + routePath, + pathParams: pathParams.length, + queryParams: Object.keys(query).length, + }); + + return { path: routePath, params, query }; + } catch (error) { + logger.error(`[DeepLink] ❌ Parse failed:`, { + url, + error: error instanceof Error ? error.message : String(error), + }); + throw error; + } } /** - * Routes the deep link to appropriate view with validated parameters. - * Validates route and parameters using Zod schemas before routing. - * - * @param path - The route path from the deep link - * @param params - URL parameters - * @param query - Query string parameters - * @throws {DeepLinkError} If validation fails or route is invalid + * Validate and route the deep link + * @param path - The route path + * @param params - Path parameters + * @param query - Query parameters */ private async validateAndRoute( path: string, params: Record<string, string>, query: Record<string, string>, ): Promise<void> { + logger.debug( + `[DeepLink] 🎯 Starting validation and routing for path: ${path}`, + ); + // First try to validate the route path let routeName: string; try { + logger.debug(`[DeepLink] 🔍 Validating route path: ${path}`); // Validate route exists const validRoute = routeSchema.parse(path) as DeepLinkRoute; - routeName = ROUTE_MAP[validRoute].name; + logger.debug(`[DeepLink] ✅ Route validation passed: ${validRoute}`); + + // Get route configuration + const routeConfig = ROUTE_MAP[validRoute]; + logger.debug(`[DeepLink] 📋 Route config retrieved:`, routeConfig); + + if (!routeConfig) { + logger.error(`[DeepLink] ❌ No route config found for: ${validRoute}`); + throw new Error(`Route configuration missing for: ${validRoute}`); + } + + routeName = routeConfig.name; + logger.debug(`[DeepLink] 🎯 Route name resolved: ${routeName}`); } catch (error) { - logger.error(`[DeepLink] Invalid route path: ${path}`); + logger.error(`[DeepLink] ❌ Route validation failed:`, { + path, + error: error instanceof Error ? error.message : String(error), + }); // Redirect to error page with information about the invalid link await this.router.replace({ @@ -193,21 +228,66 @@ export class DeepLinkHandler { }, }); - // This previously threw an error but we're redirecting so there's no need. + logger.debug( + `[DeepLink] 🔄 Redirected to error page for invalid route: ${path}`, + ); return; } - // Continue with parameter validation as before... - const schema = deepLinkSchemas[path as keyof typeof deepLinkSchemas]; + // Continue with parameter validation + logger.debug( + `[DeepLink] 🔍 Starting parameter validation for route: ${routeName}`, + ); + + const pathSchema = + deepLinkPathSchemas[path as keyof typeof deepLinkPathSchemas]; + const querySchema = + deepLinkQuerySchemas[path as keyof typeof deepLinkQuerySchemas]; + + logger.debug(`[DeepLink] 📋 Schemas found:`, { + hasPathSchema: !!pathSchema, + hasQuerySchema: !!querySchema, + pathSchemaType: pathSchema ? typeof pathSchema : "none", + querySchemaType: querySchema ? typeof querySchema : "none", + }); + + let validatedPathParams: Record<string, string> = {}; + let validatedQueryParams: Record<string, string> = {}; - let validatedParams; try { - validatedParams = await schema.parseAsync(params); + if (pathSchema) { + logger.debug(`[DeepLink] 🔍 Validating path parameters:`, params); + validatedPathParams = await pathSchema.parseAsync(params); + logger.debug( + `[DeepLink] ✅ Path parameters validated:`, + validatedPathParams, + ); + } else { + logger.debug(`[DeepLink] ⚠️ No path schema found for: ${path}`); + validatedPathParams = params; + } + + if (querySchema) { + logger.debug(`[DeepLink] 🔍 Validating query parameters:`, query); + validatedQueryParams = await querySchema.parseAsync(query); + logger.debug( + `[DeepLink] ✅ Query parameters validated:`, + validatedQueryParams, + ); + } else { + logger.debug(`[DeepLink] ⚠️ No query schema found for: ${path}`); + validatedQueryParams = query; + } } catch (error) { - // For parameter validation errors, provide specific error feedback - logger.error( - `[DeepLink] Invalid parameters for route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with params: ${JSON.stringify(params)} ... and query: ${JSON.stringify(query)}`, - ); + logger.error(`[DeepLink] ❌ Parameter validation failed:`, { + routeName, + path, + params, + query, + error: error instanceof Error ? error.message : String(error), + errorDetails: JSON.stringify(error), + }); + await this.router.replace({ name: "deep-link-error", params, @@ -219,58 +299,52 @@ export class DeepLinkHandler { }, }); - // This previously threw an error but we're redirecting so there's no need. + logger.debug( + `[DeepLink] 🔄 Redirected to error page for invalid parameters`, + ); return; } + // Attempt navigation try { + logger.debug(`[DeepLink] 🚀 Attempting navigation:`, { + routeName, + pathParams: validatedPathParams, + queryParams: validatedQueryParams, + }); + await this.router.replace({ name: routeName, - params: validatedParams, + params: validatedPathParams, + query: validatedQueryParams, }); + + logger.debug(`[DeepLink] ✅ Navigation successful to: ${routeName}`); } catch (error) { - logger.error( - `[DeepLink] Error routing to route name ${routeName} for path: ${path}: ${JSON.stringify(error)} ... with validated params: ${JSON.stringify(validatedParams)}`, - ); - // For parameter validation errors, provide specific error feedback + logger.error(`[DeepLink] ❌ Navigation failed:`, { + routeName, + path, + validatedPathParams, + validatedQueryParams, + error: error instanceof Error ? error.message : String(error), + errorDetails: JSON.stringify(error), + }); + + // Redirect to error page for navigation failures await this.router.replace({ name: "deep-link-error", - params: validatedParams, + params: validatedPathParams, query: { originalPath: path, errorCode: "ROUTING_ERROR", - errorMessage: `Error routing to ${routeName}: ${JSON.stringify(error)}`, + errorMessage: `Error routing to ${routeName}: ${(error as Error).message}`, + ...validatedQueryParams, }, }); - } - } - /** - * Processes incoming deep links and routes them appropriately. - * Handles validation, error handling, and routing to the correct view. - * - * @param url - The deep link URL to process - * @throws {DeepLinkError} If URL processing fails - */ - async handleDeepLink(url: string): Promise<void> { - try { - const { path, params, query } = this.parseDeepLink(url); - // Ensure params is always a Record<string,string> by converting undefined to empty string - const sanitizedParams = Object.fromEntries( - Object.entries(params).map(([key, value]) => [key, value ?? ""]), + logger.debug( + `[DeepLink] 🔄 Redirected to error page for navigation failure`, ); - await this.validateAndRoute(path, sanitizedParams, query); - } catch (error) { - const deepLinkError = error as DeepLinkError; - logger.error( - `[DeepLink] Error (${deepLinkError.code}): ${deepLinkError.details}`, - ); - - throw { - code: deepLinkError.code || "UNKNOWN_ERROR", - message: deepLinkError.message, - details: deepLinkError.details, - }; } } } diff --git a/src/services/platforms/CapacitorPlatformService.ts b/src/services/platforms/CapacitorPlatformService.ts index bd22ef8d..c1374f25 100644 --- a/src/services/platforms/CapacitorPlatformService.ts +++ b/src/services/platforms/CapacitorPlatformService.ts @@ -1319,7 +1319,7 @@ export class CapacitorPlatformService implements PlatformService { await this.dbExec(sql, params); } - async insertDidSpecificSettings(did: string): Promise<void> { + async insertNewDidIntoSettings(did: string): Promise<void> { await this.dbExec("INSERT INTO settings (accountDid) VALUES (?)", [did]); } diff --git a/src/services/platforms/WebPlatformService.ts b/src/services/platforms/WebPlatformService.ts index a731ee22..fb15b1b6 100644 --- a/src/services/platforms/WebPlatformService.ts +++ b/src/services/platforms/WebPlatformService.ts @@ -681,7 +681,7 @@ export class WebPlatformService implements PlatformService { await this.dbExec(sql, params); } - async insertDidSpecificSettings(did: string): Promise<void> { + async insertNewDidIntoSettings(did: string): Promise<void> { await this.dbExec("INSERT INTO settings (accountDid) VALUES (?)", [did]); } diff --git a/src/test/index.ts b/src/test/index.ts index 8974713b..914cb2be 100644 --- a/src/test/index.ts +++ b/src/test/index.ts @@ -15,10 +15,15 @@ const TEST_USER_0_MNEMONIC = "rigid shrug mobile smart veteran half all pond toilet brave review universe ship congress found yard skate elite apology jar uniform subway slender luggage"; export async function testBecomeUser0() { - const [addr, privateHex, publicHex, deriPath] = deriveAddress(TEST_USER_0_MNEMONIC); + const [addr, privateHex, publicHex, deriPath] = + deriveAddress(TEST_USER_0_MNEMONIC); const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); - await saveNewIdentity(identity0, TEST_USER_0_MNEMONIC, DEFAULT_ROOT_DERIVATION_PATH); + await saveNewIdentity( + identity0, + TEST_USER_0_MNEMONIC, + DEFAULT_ROOT_DERIVATION_PATH, + ); const platformService = await PlatformServiceFactory.getInstance(); await platformService.updateDidSpecificSettings(identity0.did, { isRegistered: true, @@ -35,7 +40,8 @@ export async function testBecomeUser0() { * @throws Error if registration fails or database access fails */ export async function testServerRegisterUser() { - const [addr, privateHex, publicHex, deriPath] = deriveAddress(TEST_USER_0_MNEMONIC); + const [addr, privateHex, publicHex, deriPath] = + deriveAddress(TEST_USER_0_MNEMONIC); const identity0 = newIdentifier(addr, publicHex, privateHex, deriPath); @@ -44,6 +50,10 @@ export async function testServerRegisterUser() { "@/db/databaseUtil" ); const settings = await retrieveSettingsForActiveAccount(); + const currentDid = settings?.activeDid; + if (!currentDid) { + throw new Error("No active DID found"); + } // Make a claim const vcClaim = { @@ -51,7 +61,7 @@ export async function testServerRegisterUser() { "@type": "RegisterAction", agent: { identifier: identity0.did }, object: SERVICE_ID, - participant: { identifier: settings.activeDid }, + participant: { identifier: currentDid }, }; // Make a payload for the claim @@ -88,5 +98,12 @@ export async function testServerRegisterUser() { const resp = await axios.post(url, payload, { headers }); logger.log("User registration result:", resp); + + const platformService = await PlatformServiceFactory.getInstance(); + await platformService.updateDefaultSettings({ activeDid: currentDid }); + await platformService.updateDidSpecificSettings(currentDid!, { + isRegistered: true, + }); + return resp; } diff --git a/src/utils/PlatformServiceMixin.ts b/src/utils/PlatformServiceMixin.ts index 68c09720..010d79ec 100644 --- a/src/utils/PlatformServiceMixin.ts +++ b/src/utils/PlatformServiceMixin.ts @@ -437,17 +437,17 @@ export const PlatformServiceMixin = { }, /** - * Utility method for retrieving and parsing settings + * Utility method for retrieving master settings * Common pattern used across many components */ - async $getSettings( - key: string, + async $getMasterSettings( fallback: Settings | null = null, ): Promise<Settings | null> { try { + // Master settings: query by id const result = await this.$dbQuery( - "SELECT * FROM settings WHERE id = ? OR accountDid = ?", - [key, key], + "SELECT * FROM settings WHERE id = ?", + [MASTER_SETTINGS_KEY], ); if (!result?.values?.length) { @@ -472,8 +472,7 @@ export const PlatformServiceMixin = { return settings; } catch (error) { - logger.error(`[Settings Trace] ❌ Failed to get settings:`, { - key, + logger.error(`[Settings Trace] ❌ Failed to get master settings:`, { error, }); return fallback; @@ -491,10 +490,7 @@ export const PlatformServiceMixin = { ): Promise<Settings> { try { // Get default settings - const defaultSettings = await this.$getSettings( - defaultKey, - defaultFallback, - ); + const defaultSettings = await this.$getMasterSettings(defaultFallback); // If no account DID, return defaults if (!accountDid) { @@ -757,19 +753,20 @@ export const PlatformServiceMixin = { * @returns Fresh settings object from database */ async $settings(defaults: Settings = {}): Promise<Settings> { - const settings = await this.$getSettings(MASTER_SETTINGS_KEY, defaults); + const settings = await this.$getMasterSettings(defaults); if (!settings) { return defaults; } - // **ELECTRON-SPECIFIC FIX**: Apply platform-specific API server override - // This ensures Electron always uses production endpoints regardless of cached settings - if (process.env.VITE_PLATFORM === "electron") { + // FIXED: Remove forced override - respect user preferences + // Only set default if no user preference exists + if (!settings.apiServer && process.env.VITE_PLATFORM === "electron") { // Import constants dynamically to get platform-specific values const { DEFAULT_ENDORSER_API_SERVER } = await import( "../constants/app" ); + // Only set if user hasn't specified a preference settings.apiServer = DEFAULT_ENDORSER_API_SERVER; } @@ -789,10 +786,7 @@ export const PlatformServiceMixin = { ): Promise<Settings> { try { // Get default settings first - const defaultSettings = await this.$getSettings( - MASTER_SETTINGS_KEY, - defaults, - ); + const defaultSettings = await this.$getMasterSettings(defaults); if (!defaultSettings) { return defaults; @@ -813,14 +807,17 @@ export const PlatformServiceMixin = { defaultSettings, ); - // **ELECTRON-SPECIFIC FIX**: Force production API endpoints for Electron - // This ensures Electron doesn't use localhost development servers that might be saved in user settings - if (process.env.VITE_PLATFORM === "electron") { + // FIXED: Remove forced override - respect user preferences + // Only set default if no user preference exists + if ( + !mergedSettings.apiServer && + process.env.VITE_PLATFORM === "electron" + ) { // Import constants dynamically to get platform-specific values const { DEFAULT_ENDORSER_API_SERVER } = await import( "../constants/app" ); - + // Only set if user hasn't specified a preference mergedSettings.apiServer = DEFAULT_ENDORSER_API_SERVER; } @@ -1574,10 +1571,7 @@ export const PlatformServiceMixin = { async $debugMergedSettings(did: string): Promise<void> { try { // Get default settings - const defaultSettings = await this.$getSettings( - MASTER_SETTINGS_KEY, - {}, - ); + const defaultSettings = await this.$getMasterSettings({}); logger.info( `[PlatformServiceMixin] Default settings:`, defaultSettings, @@ -1624,10 +1618,7 @@ export interface IPlatformServiceMixin { ): Promise<QueryExecResult | undefined>; $dbExec(sql: string, params?: unknown[]): Promise<DatabaseExecResult>; $dbGetOneRow(sql: string, params?: unknown[]): Promise<unknown[] | undefined>; - $getSettings( - key: string, - fallback?: Settings | null, - ): Promise<Settings | null>; + $getMasterSettings(fallback?: Settings | null): Promise<Settings | null>; $getMergedSettings( defaultKey: string, accountDid?: string, @@ -1749,10 +1740,7 @@ declare module "@vue/runtime-core" { sql: string, params?: unknown[], ): Promise<unknown[] | undefined>; - $getSettings( - key: string, - defaults?: Settings | null, - ): Promise<Settings | null>; + $getMasterSettings(defaults?: Settings | null): Promise<Settings | null>; $getMergedSettings( key: string, did?: string, diff --git a/src/utils/errorHandler.ts b/src/utils/errorHandler.ts new file mode 100644 index 00000000..ee608844 --- /dev/null +++ b/src/utils/errorHandler.ts @@ -0,0 +1,298 @@ +/** + * Standardized Error Handler + * + * Provides consistent error handling patterns across the TimeSafari codebase + * to improve debugging, user experience, and maintainability. + * + * @author Matthew Raymer + * @since 2025-08-25 + */ + +import { AxiosError } from "axios"; +import { logger } from "./logger"; + +/** + * Standard error context for consistent logging + */ +export interface ErrorContext { + component: string; + operation: string; + timestamp: string; + [key: string]: unknown; +} + +/** + * Enhanced error information for better debugging + */ +export interface EnhancedErrorInfo { + errorType: "AxiosError" | "NetworkError" | "ValidationError" | "UnknownError"; + status?: number; + statusText?: string; + errorData?: unknown; + errorMessage: string; + errorStack?: string; + requestContext?: { + url?: string; + method?: string; + headers?: Record<string, unknown>; + }; +} + +/** + * Standardized error handler for API operations + * + * @param error - The error that occurred + * @param context - Context information about the operation + * @param operation - Description of the operation being performed + * @returns Enhanced error information for consistent handling + */ +export function handleApiError( + error: unknown, + context: ErrorContext, + operation: string, +): EnhancedErrorInfo { + const baseContext = { + ...context, + operation, + timestamp: new Date().toISOString(), + }; + + if (error instanceof AxiosError) { + const axiosError = error as AxiosError; + const status = axiosError.response?.status; + const statusText = axiosError.response?.statusText; + const errorData = axiosError.response?.data; + + const enhancedError: EnhancedErrorInfo = { + errorType: "AxiosError", + status, + statusText, + errorData, + errorMessage: axiosError.message, + errorStack: axiosError.stack, + requestContext: { + url: axiosError.config?.url, + method: axiosError.config?.method, + headers: axiosError.config?.headers, + }, + }; + + // Log with consistent format + logger.error( + `[${context.component}] ❌ ${operation} failed (AxiosError):`, + { + ...baseContext, + ...enhancedError, + }, + ); + + return enhancedError; + } + + if (error instanceof Error) { + const enhancedError: EnhancedErrorInfo = { + errorType: "UnknownError", + errorMessage: error.message, + errorStack: error.stack, + }; + + logger.error(`[${context.component}] ❌ ${operation} failed (Error):`, { + ...baseContext, + ...enhancedError, + }); + + return enhancedError; + } + + // Handle unknown error types + const enhancedError: EnhancedErrorInfo = { + errorType: "UnknownError", + errorMessage: String(error), + }; + + logger.error(`[${context.component}] ❌ ${operation} failed (Unknown):`, { + ...baseContext, + ...enhancedError, + }); + + return enhancedError; +} + +/** + * Extract human-readable error message from various error response formats + * + * @param errorData - Error response data + * @returns Human-readable error message + */ +export function extractErrorMessage(errorData: unknown): string { + if (typeof errorData === "string") { + return errorData; + } + + if (typeof errorData === "object" && errorData !== null) { + const obj = errorData as Record<string, unknown>; + + // Try common error message fields + if (obj.message && typeof obj.message === "string") { + return obj.message; + } + + if (obj.error && typeof obj.error === "string") { + return obj.error; + } + + if (obj.detail && typeof obj.detail === "string") { + return obj.detail; + } + + if (obj.reason && typeof obj.reason === "string") { + return obj.reason; + } + + // Fallback to stringified object + return JSON.stringify(errorData); + } + + return String(errorData); +} + +/** + * Create user-friendly error message from enhanced error info + * + * @param errorInfo - Enhanced error information + * @param fallbackMessage - Fallback message if error details are insufficient + * @returns User-friendly error message + */ +export function createUserMessage( + errorInfo: EnhancedErrorInfo, + fallbackMessage: string, +): string { + if (errorInfo.errorType === "AxiosError") { + const status = errorInfo.status; + const statusText = errorInfo.statusText; + const errorMessage = extractErrorMessage(errorInfo.errorData); + + if (status && statusText) { + if (errorMessage && errorMessage !== "{}") { + return `${fallbackMessage}: ${status} ${statusText} - ${errorMessage}`; + } + return `${fallbackMessage}: ${status} ${statusText}`; + } + } + + if ( + errorInfo.errorMessage && + errorInfo.errorMessage !== "Request failed with status code 0" + ) { + return `${fallbackMessage}: ${errorInfo.errorMessage}`; + } + + return fallbackMessage; +} + +/** + * Handle specific HTTP status codes with appropriate user messages + * + * @param status - HTTP status code + * @param errorData - Error response data + * @param operation - Description of the operation + * @returns User-friendly error message + */ +export function handleHttpStatus( + status: number, + errorData: unknown, + operation: string, +): string { + const errorMessage = extractErrorMessage(errorData); + + switch (status) { + case 400: + return errorMessage || `${operation} failed: Bad request`; + case 401: + return `${operation} failed: Authentication required`; + case 403: + return `${operation} failed: Access denied`; + case 404: + return errorMessage || `${operation} failed: Resource not found`; + case 409: + return errorMessage || `${operation} failed: Conflict with existing data`; + case 422: + return errorMessage || `${operation} failed: Validation error`; + case 429: + return `${operation} failed: Too many requests. Please try again later.`; + case 500: + return `${operation} failed: Server error. Please try again later.`; + case 502: + case 503: + case 504: + return `${operation} failed: Service temporarily unavailable. Please try again later.`; + default: + return errorMessage || `${operation} failed: HTTP ${status}`; + } +} + +/** + * Check if an error is a network-related error + * + * @param error - The error to check + * @returns True if the error is network-related + */ +export function isNetworkError(error: unknown): boolean { + if (error instanceof AxiosError) { + return !error.response && !error.request; + } + + if (error instanceof Error) { + const message = error.message.toLowerCase(); + return ( + message.includes("network") || + message.includes("timeout") || + message.includes("connection") || + message.includes("fetch") + ); + } + + return false; +} + +/** + * Check if an error is a timeout error + * + * @param error - The error to check + * @returns True if the error is a timeout + */ +export function isTimeoutError(error: unknown): boolean { + if (error instanceof AxiosError) { + return ( + error.code === "ECONNABORTED" || + error.message.toLowerCase().includes("timeout") + ); + } + + if (error instanceof Error) { + return error.message.toLowerCase().includes("timeout"); + } + + return false; +} + +/** + * Create standardized error context for components + * + * @param component - Component name + * @param operation - Operation being performed + * @param additionalContext - Additional context information + * @returns Standardized error context + */ +export function createErrorContext( + component: string, + operation: string, + additionalContext: Record<string, unknown> = {}, +): ErrorContext { + return { + component, + operation, + timestamp: new Date().toISOString(), + ...additionalContext, + }; +} diff --git a/src/utils/performanceOptimizer.ts b/src/utils/performanceOptimizer.ts new file mode 100644 index 00000000..8a77c599 --- /dev/null +++ b/src/utils/performanceOptimizer.ts @@ -0,0 +1,482 @@ +/** + * Performance Optimizer + * + * Provides utilities for optimizing API calls, database queries, and component + * rendering to improve TimeSafari application performance. + * + * @author Matthew Raymer + * @since 2025-08-25 + */ + +import { logger } from "./logger"; + +/** + * Batch operation configuration + */ +export interface BatchConfig { + maxBatchSize: number; + maxWaitTime: number; + retryAttempts: number; + retryDelay: number; +} + +/** + * Default batch configuration + */ +export const DEFAULT_BATCH_CONFIG: BatchConfig = { + maxBatchSize: 10, + maxWaitTime: 100, // milliseconds + retryAttempts: 3, + retryDelay: 1000, // milliseconds +}; + +/** + * Batched operation item + */ +export interface BatchItem<T, R> { + id: string; + data: T; + resolve: (value: R) => void; + reject: (error: Error) => void; + timestamp: number; +} + +/** + * Batch processor for API operations + * + * Groups multiple similar operations into batches to reduce + * the number of API calls and improve performance. + */ +export class BatchProcessor<T, R> { + private items: BatchItem<T, R>[] = []; + private timer: NodeJS.Timeout | null = null; + private processing = false; + private config: BatchConfig; + + constructor( + private batchHandler: (items: T[]) => Promise<R[]>, + private itemIdExtractor: (item: T) => string, + config: Partial<BatchConfig> = {}, + ) { + this.config = { ...DEFAULT_BATCH_CONFIG, ...config }; + } + + /** + * Add an item to the batch + * + * @param data - Data to process + * @returns Promise that resolves when the item is processed + */ + async add(data: T): Promise<R> { + return new Promise((resolve, reject) => { + const item: BatchItem<T, R> = { + id: this.itemIdExtractor(data), + data, + resolve, + reject, + timestamp: Date.now(), + }; + + this.items.push(item); + + // Start timer if this is the first item + if (this.items.length === 1) { + this.startTimer(); + } + + // Process immediately if batch is full + if (this.items.length >= this.config.maxBatchSize) { + this.processBatch(); + } + }); + } + + /** + * Start the batch timer + */ + private startTimer(): void { + if (this.timer) { + clearTimeout(this.timer); + } + + this.timer = setTimeout(() => { + this.processBatch(); + }, this.config.maxWaitTime); + } + + /** + * Process the current batch + */ + private async processBatch(): Promise<void> { + if (this.processing || this.items.length === 0) { + return; + } + + this.processing = true; + + // Clear timer + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + + // Get current batch + const currentItems = [...this.items]; + this.items = []; + + try { + logger.debug("[BatchProcessor] 🔄 Processing batch:", { + batchSize: currentItems.length, + itemIds: currentItems.map((item) => item.id), + timestamp: new Date().toISOString(), + }); + + // Process batch + const results = await this.batchHandler( + currentItems.map((item) => item.data), + ); + + // Map results back to items + const resultMap = new Map<string, R>(); + results.forEach((result, index) => { + const item = currentItems[index]; + if (item) { + resultMap.set(item.id, result); + } + }); + + // Resolve promises + currentItems.forEach((item) => { + const result = resultMap.get(item.id); + if (result !== undefined) { + item.resolve(result); + } else { + item.reject(new Error(`No result found for item ${item.id}`)); + } + }); + + logger.debug("[BatchProcessor] ✅ Batch processed successfully:", { + batchSize: currentItems.length, + resultsCount: results.length, + timestamp: new Date().toISOString(), + }); + } catch (error) { + logger.error("[BatchProcessor] ❌ Batch processing failed:", { + batchSize: currentItems.length, + error: error instanceof Error ? error.message : String(error), + timestamp: new Date().toISOString(), + }); + + // Reject all items in the batch + currentItems.forEach((item) => { + item.reject(error instanceof Error ? error : new Error(String(error))); + }); + } finally { + this.processing = false; + + // Start timer for remaining items if any + if (this.items.length > 0) { + this.startTimer(); + } + } + } + + /** + * Get current batch status + */ + getStatus(): { + pendingItems: number; + isProcessing: boolean; + hasTimer: boolean; + } { + return { + pendingItems: this.items.length, + isProcessing: this.processing, + hasTimer: this.timer !== null, + }; + } + + /** + * Clear all pending items + */ + clear(): void { + if (this.timer) { + clearTimeout(this.timer); + this.timer = null; + } + + // Reject all pending items + this.items.forEach((item) => { + item.reject(new Error("Batch processor cleared")); + }); + + this.items = []; + this.processing = false; + } +} + +/** + * Database query optimizer + * + * Provides utilities for optimizing database queries and reducing + * the number of database operations. + */ +export class DatabaseOptimizer { + /** + * Batch multiple SELECT queries into a single query + * + * @param baseQuery - Base SELECT query + * @param ids - Array of IDs to query + * @param idColumn - Name of the ID column + * @returns Optimized query string + */ + static batchSelectQuery( + baseQuery: string, + ids: (string | number)[], + idColumn: string, + ): string { + if (ids.length === 0) { + return baseQuery; + } + + if (ids.length === 1) { + return `${baseQuery} WHERE ${idColumn} = ?`; + } + + const placeholders = ids.map(() => "?").join(", "); + return `${baseQuery} WHERE ${idColumn} IN (${placeholders})`; + } + + /** + * Create a query plan for multiple operations + * + * @param operations - Array of database operations + * @returns Optimized query plan + */ + static createQueryPlan( + operations: Array<{ + type: "SELECT" | "INSERT" | "UPDATE" | "DELETE"; + table: string; + priority: number; + }>, + ): Array<{ + type: "SELECT" | "INSERT" | "UPDATE" | "DELETE"; + table: string; + priority: number; + batchable: boolean; + }> { + return operations + .map((op) => ({ + ...op, + batchable: op.type === "SELECT" || op.type === "INSERT", + })) + .sort((a, b) => { + // Sort by priority first, then by type + if (a.priority !== b.priority) { + return b.priority - a.priority; + } + + // SELECT operations first, then INSERT, UPDATE, DELETE + const typeOrder = { SELECT: 0, INSERT: 1, UPDATE: 2, DELETE: 3 }; + return typeOrder[a.type] - typeOrder[b.type]; + }); + } +} + +/** + * Component rendering optimizer + * + * Provides utilities for optimizing Vue component rendering + * and reducing unnecessary re-renders. + */ +export class ComponentOptimizer { + /** + * Debounce function calls to prevent excessive execution + * + * @param func - Function to debounce + * @param wait - Wait time in milliseconds + * @returns Debounced function + */ + static debounce<T extends (...args: unknown[]) => unknown>( + func: T, + wait: number, + ): (...args: Parameters<T>) => void { + let timeout: NodeJS.Timeout | null = null; + + return (...args: Parameters<T>) => { + if (timeout) { + clearTimeout(timeout); + } + + timeout = setTimeout(() => { + func(...args); + }, wait); + }; + } + + /** + * Throttle function calls to limit execution frequency + * + * @param func - Function to throttle + * @param limit - Time limit in milliseconds + * @returns Throttled function + */ + static throttle<T extends (...args: unknown[]) => unknown>( + func: T, + limit: number, + ): (...args: Parameters<T>) => void { + let inThrottle = false; + + return (...args: Parameters<T>) => { + if (!inThrottle) { + func(...args); + inThrottle = true; + setTimeout(() => { + inThrottle = false; + }, limit); + } + }; + } + + /** + * Memoize function results to avoid redundant computation + * + * @param func - Function to memoize + * @param keyGenerator - Function to generate cache keys + * @returns Memoized function + */ + static memoize<T extends (...args: unknown[]) => unknown, K>( + func: T, + keyGenerator: (...args: Parameters<T>) => K, + ): T { + const cache = new Map<K, unknown>(); + + return ((...args: Parameters<T>) => { + const key = keyGenerator(...args); + + if (cache.has(key)) { + return cache.get(key); + } + + const result = func(...args); + cache.set(key, result); + return result; + }) as T; + } +} + +/** + * Performance monitoring utility + * + * Tracks and reports performance metrics for optimization analysis. + */ +export class PerformanceMonitor { + private static instance: PerformanceMonitor; + private metrics = new Map< + string, + Array<{ timestamp: number; duration: number }> + >(); + + private constructor() {} + + /** + * Get singleton instance + */ + static getInstance(): PerformanceMonitor { + if (!PerformanceMonitor.instance) { + PerformanceMonitor.instance = new PerformanceMonitor(); + } + return PerformanceMonitor.instance; + } + + /** + * Start timing an operation + * + * @param operationName - Name of the operation + * @returns Function to call when operation completes + */ + startTiming(operationName: string): () => void { + const startTime = performance.now(); + + return () => { + const duration = performance.now() - startTime; + this.recordMetric(operationName, duration); + }; + } + + /** + * Record a performance metric + * + * @param operationName - Name of the operation + * @param duration - Duration in milliseconds + */ + private recordMetric(operationName: string, duration: number): void { + if (!this.metrics.has(operationName)) { + this.metrics.set(operationName, []); + } + + const operationMetrics = this.metrics.get(operationName)!; + operationMetrics.push({ + timestamp: Date.now(), + duration, + }); + + // Keep only last 100 metrics per operation + if (operationMetrics.length > 100) { + operationMetrics.splice(0, operationMetrics.length - 100); + } + } + + /** + * Get performance summary for an operation + * + * @param operationName - Name of the operation + * @returns Performance statistics + */ + getPerformanceSummary(operationName: string): { + count: number; + average: number; + min: number; + max: number; + recentAverage: number; + } | null { + const metrics = this.metrics.get(operationName); + if (!metrics || metrics.length === 0) { + return null; + } + + const durations = metrics.map((m) => m.duration); + const recentMetrics = metrics.slice(-10); // Last 10 metrics + + return { + count: metrics.length, + average: durations.reduce((a, b) => a + b, 0) / durations.length, + min: Math.min(...durations), + max: Math.max(...durations), + recentAverage: + recentMetrics.reduce((a, b) => a + b.duration, 0) / + recentMetrics.length, + }; + } + + /** + * Get all performance metrics + */ + getAllMetrics(): Map<string, Array<{ timestamp: number; duration: number }>> { + return new Map(this.metrics); + } + + /** + * Clear all performance metrics + */ + clearMetrics(): void { + this.metrics.clear(); + } +} + +/** + * Convenience function to get the performance monitor + */ +export const getPerformanceMonitor = (): PerformanceMonitor => { + return PerformanceMonitor.getInstance(); +}; diff --git a/src/utils/safeAreaInset.js b/src/utils/safeAreaInset.js new file mode 100644 index 00000000..036f0ef6 --- /dev/null +++ b/src/utils/safeAreaInset.js @@ -0,0 +1,226 @@ +/** + * Safe Area Inset Injection for Android WebView + * + * This script injects safe area inset values into CSS environment variables + * when running in Android WebView, since Android doesn't natively support + * CSS env(safe-area-inset-*) variables like iOS does. + */ + +// Check if we're running in Android WebView with Capacitor +const isAndroidWebView = () => { + // Check if we're on iOS - if so, skip this script entirely + const isIOS = + /iPad|iPhone|iPod/.test(navigator.userAgent) || + (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1); + + if (isIOS) { + return false; + } + + // Check if we're on Android + const isAndroid = /Android/.test(navigator.userAgent); + + // Check if we have Capacitor (required for Android WebView) + const hasCapacitor = window.Capacitor !== undefined; + + // Only run on Android with Capacitor + return isAndroid && hasCapacitor; +}; + +// Wait for Capacitor to be available +const waitForCapacitor = () => { + return new Promise((resolve) => { + if (window.Capacitor) { + resolve(window.Capacitor); + return; + } + + // Wait for Capacitor to be available + const checkCapacitor = () => { + if (window.Capacitor) { + resolve(window.Capacitor); + } else { + setTimeout(checkCapacitor, 100); + } + }; + + checkCapacitor(); + }); +}; + +// Inject safe area inset values into CSS custom properties +const injectSafeAreaInsets = async () => { + try { + // Wait for Capacitor to be available + const Capacitor = await waitForCapacitor(); + + // Try to get safe area insets using StatusBar plugin (which is already available) + + let top = 0, + bottom = 0, + left = 0, + right = 0; + + try { + // Use StatusBar plugin to get status bar height + if (Capacitor.Plugins.StatusBar) { + const statusBarInfo = await Capacitor.Plugins.StatusBar.getInfo(); + // Status bar height is typically the top safe area inset + top = statusBarInfo.overlays ? 0 : statusBarInfo.height || 0; + } + } catch (error) { + // Status bar info not available, will use fallback + } + + // Detect navigation bar and gesture bar heights + const detectNavigationBar = () => { + const screenHeight = window.screen.height; + const screenWidth = window.screen.width; + const windowHeight = window.innerHeight; + const devicePixelRatio = window.devicePixelRatio || 1; + + // Calculate navigation bar height + let navBarHeight = 0; + + // Method 1: Direct comparison (most reliable) + if (windowHeight < screenHeight) { + navBarHeight = screenHeight - windowHeight; + } + + // Method 2: Check for gesture navigation indicators + if (navBarHeight === 0) { + // Look for common gesture navigation patterns + const isTallDevice = screenHeight > 2000; + const isModernDevice = screenHeight > 1800; + const hasHighDensity = devicePixelRatio >= 2.5; + + if (isTallDevice && hasHighDensity) { + // Modern gesture-based device + navBarHeight = 12; // Typical gesture bar height + } else if (isModernDevice) { + // Modern device with traditional navigation + navBarHeight = 48; // Traditional navigation bar height + } + } + + // Method 3: Check visual viewport (more accurate for WebView) + if (navBarHeight === 0) { + if (window.visualViewport) { + const visualHeight = window.visualViewport.height; + + if (visualHeight < windowHeight) { + navBarHeight = windowHeight - visualHeight; + } + } + } + + // Method 4: Device-specific estimation based on screen dimensions + if (navBarHeight === 0) { + // Common Android navigation bar heights in pixels + const commonNavBarHeights = { + "1080x2400": 48, // Common 1080p devices + "1440x3200": 64, // QHD devices + "720x1600": 32, // HD devices + }; + + const resolution = `${screenWidth}x${screenHeight}`; + const estimatedHeight = commonNavBarHeights[resolution]; + + if (estimatedHeight) { + navBarHeight = estimatedHeight; + } else { + // Fallback: estimate based on screen height + navBarHeight = screenHeight > 2000 ? 48 : 32; + } + } + + return navBarHeight; + }; + + // Get navigation bar height + bottom = detectNavigationBar(); + + // If we still don't have a top value, estimate it + if (top === 0) { + const screenHeight = window.screen.height; + // Common status bar heights: 24dp (48px) for most devices, 32dp (64px) for some + top = screenHeight > 1920 ? 64 : 48; + } + + // Left/right safe areas are rare on Android + left = 0; + right = 0; + + // Create CSS custom properties + const style = document.createElement("style"); + style.textContent = ` + :root { + --safe-area-inset-top: ${top}px; + --safe-area-inset-bottom: ${bottom}px; + --safe-area-inset-left: ${left}px; + --safe-area-inset-right: ${right}px; + } + `; + + // Inject the style into the document head + document.head.appendChild(style); + + // Also set CSS environment variables if supported + if (CSS.supports("env(safe-area-inset-top)")) { + document.documentElement.style.setProperty( + "--env-safe-area-inset-top", + `${top}px`, + ); + document.documentElement.style.setProperty( + "--env-safe-area-inset-bottom", + `${bottom}px`, + ); + document.documentElement.style.setProperty( + "--env-safe-area-inset-left", + `${left}px`, + ); + document.documentElement.style.setProperty( + "--env-safe-area-inset-right", + `${right}px`, + ); + } + } catch (error) { + // Error injecting safe area insets, will use fallback values + } +}; + +// Initialize when DOM is ready +const initializeSafeArea = () => { + // Check if we should run this script at all + if (!isAndroidWebView()) { + return; + } + + // Add a small delay to ensure WebView is fully initialized + setTimeout(() => { + injectSafeAreaInsets(); + }, 100); +}; + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initializeSafeArea); +} else { + initializeSafeArea(); +} + +// Re-inject on orientation change (only on Android) +window.addEventListener("orientationchange", () => { + if (isAndroidWebView()) { + setTimeout(() => injectSafeAreaInsets(), 100); + } +}); + +// Re-inject on resize (only on Android) +window.addEventListener("resize", () => { + if (isAndroidWebView()) { + setTimeout(() => injectSafeAreaInsets(), 100); + } +}); + +// Export for use in other modules +export { injectSafeAreaInsets, isAndroidWebView }; diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 81c2978e..e68efca2 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -55,9 +55,13 @@ <!-- Registration notice --> <RegistrationNotice - :is-registered="isRegistered" - :show="showRegistrationNotice" - @share-info="onShareInfo" + v-if="!isRegistered" + :passkeys-enabled="PASSKEYS_ENABLED" + :given-name="givenName" + :message=" + `Before you can publicly announce a new project or time commitment, ` + + `a friend needs to register you.` + " /> <!-- Notifications --> @@ -174,11 +178,12 @@ :aria-busy="loadingProfile || savingProfile" ></textarea> - <div class="flex items-center mb-4" @click="toggleUserProfileLocation"> + <div class="flex items-center mb-4"> <input v-model="includeUserProfileLocation" type="checkbox" class="mr-2" + @change="onLocationCheckboxChange" /> <label for="includeUserProfileLocation">Include Location</label> </div> @@ -194,6 +199,7 @@ class="!z-40 rounded-md" @click="onProfileMapClick" @ready="onMapReady" + @mounted="onMapMounted" > <l-tile-layer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" @@ -748,9 +754,11 @@ import "leaflet/dist/leaflet.css"; import { Buffer } from "buffer/"; import "dexie-export-import"; + // @ts-expect-error - they aren't exporting it but it's there import { ImportProgress } from "dexie-export-import"; import { LeafletMouseEvent } from "leaflet"; +import * as L from "leaflet"; import * as R from "ramda"; import { IIdentifier } from "@veramo/core"; import { ref } from "vue"; @@ -778,6 +786,7 @@ import { DEFAULT_PUSH_SERVER, IMAGE_TYPE_PROFILE, NotificationIface, + PASSKEYS_ENABLED, } from "../constants/app"; import { Contact } from "../db/tables/contacts"; import { @@ -807,11 +816,13 @@ import { isApiError, ImportContent, } from "@/interfaces/accountView"; -import { - ProfileService, - createProfileService, - ProfileData, -} from "@/services/ProfileService"; +// Profile data interface (inlined from ProfileService) +interface ProfileData { + description: string; + latitude: number; + longitude: number; + includeLocation: boolean; +} const inputImportFileNameRef = ref<Blob>(); @@ -848,6 +859,7 @@ export default class AccountViewView extends Vue { readonly DEFAULT_PUSH_SERVER: string = DEFAULT_PUSH_SERVER; readonly DEFAULT_IMAGE_API_SERVER: string = DEFAULT_IMAGE_API_SERVER; readonly DEFAULT_PARTNER_API_SERVER: string = DEFAULT_PARTNER_API_SERVER; + readonly PASSKEYS_ENABLED: boolean = PASSKEYS_ENABLED; // Identity and settings properties activeDid: string = ""; @@ -902,17 +914,37 @@ export default class AccountViewView extends Vue { warnIfProdServer: boolean = false; warnIfTestServer: boolean = false; zoom: number = 2; + isMapReady: boolean = false; // Limits and validation properties endorserLimits: EndorserRateLimits | null = null; imageLimits: ImageRateLimits | null = null; limitsMessage: string = ""; - private profileService!: ProfileService; private notify!: ReturnType<typeof createNotifyHelpers>; created() { this.notify = createNotifyHelpers(this.$notify); + + // Fix Leaflet icon issues in modern bundlers + // This prevents the "Cannot read properties of undefined (reading 'Default')" error + if (L.Icon.Default) { + // Type-safe way to handle Leaflet icon prototype + const iconDefault = L.Icon.Default.prototype as unknown as Record< + string, + unknown + >; + if ("_getIconUrl" in iconDefault) { + delete iconDefault._getIconUrl; + } + L.Icon.Default.mergeOptions({ + iconRetinaUrl: + "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon-2x.png", + iconUrl: "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png", + shadowUrl: + "https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png", + }); + } } /** @@ -923,26 +955,36 @@ export default class AccountViewView extends Vue { * @throws Will display specific messages to the user based on different errors. */ async mounted(): Promise<void> { - this.profileService = createProfileService( - this.axios, - this.partnerApiServer, - ); try { await this.initializeState(); await this.processIdentity(); + // Profile service logic now inlined - no need for external service + logger.debug( + "[AccountViewView] Profile logic ready with partnerApiServer:", + { + partnerApiServer: this.partnerApiServer, + }, + ); + if (this.isRegistered) { try { - const profile = await this.profileService.loadProfile(this.activeDid); + const profile = await this.loadProfile(this.activeDid); if (profile) { this.userProfileDesc = profile.description; this.userProfileLatitude = profile.latitude; this.userProfileLongitude = profile.longitude; this.includeUserProfileLocation = profile.includeLocation; + + // Initialize map ready state if location is included + if (profile.includeLocation) { + this.isMapReady = false; // Will be set to true when map is ready + } } else { // Profile not created yet; leave defaults } } catch (error) { + logger.error("Error loading profile:", error); this.notify.error( ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_AVAILABLE, ); @@ -1380,21 +1422,24 @@ export default class AccountViewView extends Vue { async checkLimits(): Promise<void> { this.loadingLimits = true; - try { - const did = this.activeDid; - - if (!did) { - this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IDENTIFIER; - return; - } + const did = this.activeDid; + if (!did) { + this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.NO_IDENTIFIER; + return; + } + try { await this.$saveUserSettings(did, { apiServer: this.apiServer, partnerApiServer: this.partnerApiServer, webPushServer: this.webPushServer, }); - const imageResp = await fetchImageRateLimits(this.axios, did); + const imageResp = await fetchImageRateLimits( + this.axios, + did, + this.DEFAULT_IMAGE_API_SERVER, + ); if (imageResp.status === 200) { this.imageLimits = imageResp.data; @@ -1420,7 +1465,26 @@ export default class AccountViewView extends Vue { } catch (error) { this.limitsMessage = ACCOUNT_VIEW_CONSTANTS.LIMITS.ERROR_RETRIEVING_LIMITS; - logger.error("Error retrieving limits: ", error); + + // Enhanced error logging with server context + const axiosError = error as { + response?: { + data?: { error?: { code?: string; message?: string } }; + status?: number; + }; + }; + logger.error("[Server Limits] Error retrieving limits:", { + error: error instanceof Error ? error.message : String(error), + did: did, + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + errorCode: axiosError?.response?.data?.error?.code, + errorMessage: axiosError?.response?.data?.error?.message, + httpStatus: axiosError?.response?.status, + needsUserMigration: true, + timestamp: new Date().toISOString(), + }); + // this.notify.error(this.limitsMessage, TIMEOUTS.STANDARD); } finally { this.loadingLimits = false; @@ -1428,24 +1492,70 @@ export default class AccountViewView extends Vue { } async onClickSaveApiServer(): Promise<void> { + // Enhanced diagnostic logging for claim URL changes + const previousApiServer = this.apiServer; + const newApiServer = this.apiServerInput; + + logger.debug("[Server Switching] Claim URL change initiated:", { + did: this.activeDid, + previousServer: previousApiServer, + newServer: newApiServer, + changeType: "apiServer", + timestamp: new Date().toISOString(), + }); + await this.$saveSettings({ - apiServer: this.apiServerInput, + apiServer: newApiServer, }); - this.apiServer = this.apiServerInput; + this.apiServer = newApiServer; + // Add this line to save to user-specific settings await this.$saveUserSettings(this.activeDid, { apiServer: this.apiServer, }); + + // Log successful server switch + logger.debug("[Server Switching] Claim URL change completed:", { + did: this.activeDid, + previousServer: previousApiServer, + newServer: newApiServer, + changeType: "apiServer", + settingsSaved: true, + timestamp: new Date().toISOString(), + }); } async onClickSavePartnerServer(): Promise<void> { + // Enhanced diagnostic logging for partner server changes + const previousPartnerServer = this.partnerApiServer; + const newPartnerServer = this.partnerApiServerInput; + + logger.debug("[Server Switching] Partner server change initiated:", { + did: this.activeDid, + previousServer: previousPartnerServer, + newServer: newPartnerServer, + changeType: "partnerApiServer", + timestamp: new Date().toISOString(), + }); + await this.$saveSettings({ - partnerApiServer: this.partnerApiServerInput, + partnerApiServer: newPartnerServer, }); - this.partnerApiServer = this.partnerApiServerInput; + this.partnerApiServer = newPartnerServer; + await this.$saveUserSettings(this.activeDid, { partnerApiServer: this.partnerApiServer, }); + + // Log successful partner server switch + logger.debug("[Server Switching] Partner server change completed:", { + did: this.activeDid, + previousServer: previousPartnerServer, + newServer: newPartnerServer, + changeType: "partnerApiServer", + settingsSaved: true, + timestamp: new Date().toISOString(), + }); } async onClickSavePushServer(): Promise<void> { @@ -1518,9 +1628,46 @@ export default class AccountViewView extends Vue { } onMapReady(map: L.Map): void { - // doing this here instead of on the l-map element avoids a recentering after a drag then zoom at startup - const zoom = this.userProfileLatitude && this.userProfileLongitude ? 12 : 2; - map.setView([this.userProfileLatitude, this.userProfileLongitude], zoom); + try { + // doing this here instead of on the l-map element avoids a recentering after a drag then zoom at startup + const zoom = + this.userProfileLatitude && this.userProfileLongitude ? 12 : 2; + const lat = this.userProfileLatitude || 0; + const lng = this.userProfileLongitude || 0; + map.setView([lat, lng], zoom); + this.isMapReady = true; + logger.debug( + "Map ready state set to true, coordinates:", + [lat, lng], + "zoom:", + zoom, + ); + } catch (error) { + logger.error("Error in onMapReady:", error); + this.isMapReady = true; // Set to true even on error to prevent infinite loading + } + } + + onMapMounted(): void { + logger.debug("Map component mounted"); + // Check if map ref is available + const mapRef = this.$refs.profileMap; + logger.debug("Map ref:", mapRef); + + // Try to set map ready after component is mounted + setTimeout(() => { + this.isMapReady = true; + }, 500); + } + + // Fallback method to handle map initialization failures + private handleMapInitFailure(): void { + setTimeout(() => { + if (!this.isMapReady) { + logger.warn("Map failed to initialize, forcing ready state"); + this.isMapReady = true; + } + }, 5000); // 5 second timeout } showProfileInfo(): void { @@ -1532,14 +1679,17 @@ export default class AccountViewView extends Vue { async saveProfile(): Promise<void> { this.savingProfile = true; - const profileData: ProfileData = { - description: this.userProfileDesc, - latitude: this.userProfileLatitude, - longitude: this.userProfileLongitude, - includeLocation: this.includeUserProfileLocation, - }; try { - const success = await this.profileService.saveProfile( + const profileData: ProfileData = { + description: this.userProfileDesc, + latitude: this.userProfileLatitude, + longitude: this.userProfileLongitude, + includeLocation: this.includeUserProfileLocation, + }; + + logger.debug("Saving profile data:", profileData); + + const success = await this.saveProfileToServer( this.activeDid, profileData, ); @@ -1549,6 +1699,7 @@ export default class AccountViewView extends Vue { this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR); } } catch (error) { + logger.error("Error saving profile:", error); this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_SAVE_ERROR); } finally { this.savingProfile = false; @@ -1556,15 +1707,25 @@ export default class AccountViewView extends Vue { } toggleUserProfileLocation(): void { - const updated = this.profileService.toggleProfileLocation({ - description: this.userProfileDesc, - latitude: this.userProfileLatitude, - longitude: this.userProfileLongitude, - includeLocation: this.includeUserProfileLocation, - }); - this.userProfileLatitude = updated.latitude; - this.userProfileLongitude = updated.longitude; - this.includeUserProfileLocation = updated.includeLocation; + try { + const updated = this.toggleProfileLocation({ + description: this.userProfileDesc, + latitude: this.userProfileLatitude, + longitude: this.userProfileLongitude, + includeLocation: this.includeUserProfileLocation, + }); + this.userProfileLatitude = updated.latitude; + this.userProfileLongitude = updated.longitude; + this.includeUserProfileLocation = updated.includeLocation; + + // Reset map ready state when toggling location + if (!updated.includeLocation) { + this.isMapReady = false; + } + } catch (error) { + logger.error("Error in toggleUserProfileLocation:", error); + this.notify.error("Failed to toggle location setting"); + } } confirmEraseLatLong(): void { @@ -1592,18 +1753,26 @@ export default class AccountViewView extends Vue { async deleteProfile(): Promise<void> { try { - const success = await this.profileService.deleteProfile(this.activeDid); + const success = await this.deleteProfileFromServer(this.activeDid); if (success) { this.notify.success(ACCOUNT_VIEW_CONSTANTS.SUCCESS.PROFILE_DELETED); this.userProfileDesc = ""; this.userProfileLatitude = 0; this.userProfileLongitude = 0; this.includeUserProfileLocation = false; + this.isMapReady = false; // Reset map state } else { this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_DELETE_ERROR); } } catch (error) { - this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_DELETE_ERROR); + logger.error("Error in deleteProfile component method:", error); + + // Show more specific error message if available + if (error instanceof Error) { + this.notify.error(error.message); + } else { + this.notify.error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_DELETE_ERROR); + } } } @@ -1616,8 +1785,44 @@ export default class AccountViewView extends Vue { } onProfileMapClick(event: LeafletMouseEvent) { - this.userProfileLatitude = event.latlng.lat; - this.userProfileLongitude = event.latlng.lng; + try { + if (event && event.latlng) { + this.userProfileLatitude = event.latlng.lat; + this.userProfileLongitude = event.latlng.lng; + } + } catch (error) { + logger.error("Error in onProfileMapClick:", error); + } + } + + onLocationCheckboxChange(): void { + try { + logger.debug( + "Location checkbox changed, new value:", + this.includeUserProfileLocation, + ); + if (!this.includeUserProfileLocation) { + // Location checkbox was unchecked, clean up map state + this.isMapReady = false; + this.userProfileLatitude = 0; + this.userProfileLongitude = 0; + } else { + // Location checkbox was checked, start map initialization timeout + this.isMapReady = false; + logger.debug("Location checked, starting map initialization timeout"); + + // Try to set map ready after a short delay to allow Vue to render + setTimeout(() => { + if (!this.isMapReady) { + this.isMapReady = true; + } + }, 1000); // 1 second delay + + this.handleMapInitFailure(); + } + } catch (error) { + logger.error("Error in onLocationCheckboxChange:", error); + } } // IdentitySection event handlers @@ -1658,22 +1863,341 @@ export default class AccountViewView extends Vue { this.doCopyTwoSecRedo(did, () => (this.showDidCopy = !this.showDidCopy)); } - get showRegistrationNotice(): boolean { - // Show the notice if not registered and any other conditions you want - return !this.isRegistered; + onRecheckLimits() { + this.checkLimits(); } - onShareInfo() { - // Navigate to QR code sharing page - mobile uses full scan, web uses basic - if (Capacitor.isNativePlatform()) { - this.$router.push({ name: "contact-qr-scan-full" }); - } else { - this.$router.push({ name: "contact-qr" }); + // Inlined profile methods (previously in ProfileService) + + /** + * Load user profile from the partner API + */ + private async loadProfile(did: string): Promise<ProfileData | null> { + try { + const requestId = `profile_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + logger.debug("[AccountViewView] Loading profile:", { + requestId, + did, + partnerApiServer: this.partnerApiServer, + }); + + // Get authentication headers + const headers = await getHeaders(did); + + const fullUrl = `${this.partnerApiServer}/api/partner/userProfileForIssuer/${did}`; + + logger.debug("[AccountViewView] Making API request:", { + requestId, + did, + fullUrl, + hasAuthHeader: !!headers.Authorization, + }); + + const response = await this.axios.get(fullUrl, { headers }); + + logger.debug("[AccountViewView] Profile loaded successfully:", { + requestId, + status: response.status, + hasData: !!response.data, + }); + + if (response.data && response.data.data) { + const profileData = response.data.data; + logger.debug("[AccountViewView] Parsing profile data:", { + requestId, + locLat: profileData.locLat, + locLon: profileData.locLon, + description: profileData.description, + }); + + const result = { + description: profileData.description || "", + latitude: profileData.locLat || 0, + longitude: profileData.locLon || 0, + includeLocation: !!(profileData.locLat && profileData.locLon), + }; + + logger.debug("[AccountViewView] Parsed profile result:", { + requestId, + result, + hasLocation: result.includeLocation, + }); + + return result; + } else { + logger.debug("[AccountViewView] No profile data found in response:", { + requestId, + hasData: !!response.data, + hasDataData: !!(response.data && response.data.data), + }); + } + + return null; + } catch (error: unknown) { + // Handle specific HTTP status codes cleanly to suppress console spam + if (error && typeof error === "object" && "response" in error) { + const axiosError = error as { response?: { status?: number } }; + + if (axiosError.response?.status === 404) { + logger.info( + "[Profile] No profile found - this is normal for new users", + { + did, + server: this.partnerApiServer, + status: 404, + timestamp: new Date().toISOString(), + }, + ); + return null; + } + + if (axiosError.response?.status === 400) { + logger.warn("[Profile] Bad request - user may not be registered", { + did, + server: this.partnerApiServer, + status: 400, + timestamp: new Date().toISOString(), + }); + return null; + } + + if ( + axiosError.response?.status === 401 || + axiosError.response?.status === 403 + ) { + logger.warn("[Profile] Authentication/authorization issue", { + did, + server: this.partnerApiServer, + status: axiosError.response.status, + timestamp: new Date().toISOString(), + }); + return null; + } + } + + // Only log full errors for unexpected issues (5xx, network errors, etc.) + logger.error("[Profile] Unexpected error loading profile:", { + did, + server: this.partnerApiServer, + error: error instanceof Error ? error.message : String(error), + timestamp: new Date().toISOString(), + }); + throw new Error("Failed to load profile"); } } - onRecheckLimits() { - this.checkLimits(); + /** + * Save user profile to the partner API + */ + private async saveProfileToServer( + did: string, + profileData: ProfileData, + ): Promise<boolean> { + try { + const requestId = `profile_save_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + logger.debug("[AccountViewView] Saving profile:", { + requestId, + did, + profileData, + }); + + // Get authentication headers + const headers = await getHeaders(did); + + // Prepare payload in the format expected by the partner API + const payload = { + description: profileData.description, + issuerDid: did, + ...(profileData.includeLocation && + profileData.latitude && + profileData.longitude + ? { + locLat: profileData.latitude, + locLon: profileData.longitude, + } + : {}), + }; + + logger.debug("[AccountViewView] Sending payload to server:", { + requestId, + payload, + hasLocation: profileData.includeLocation, + }); + + const response = await this.axios.post( + `${this.partnerApiServer}/api/partner/userProfile`, + payload, + { headers }, + ); + + logger.debug("[AccountViewView] Profile saved successfully:", { + requestId, + status: response.status, + }); + + return true; + } catch (error: unknown) { + // Handle specific HTTP status codes cleanly to suppress console spam + if (error && typeof error === "object" && "response" in error) { + const axiosError = error as { response?: { status?: number } }; + + if (axiosError.response?.status === 400) { + logger.warn("[Profile] Bad request saving profile", { + did, + server: this.partnerApiServer, + status: 400, + timestamp: new Date().toISOString(), + }); + throw new Error("Invalid profile data"); + } + + if ( + axiosError.response?.status === 401 || + axiosError.response?.status === 403 + ) { + logger.warn( + "[Profile] Authentication/authorization issue saving profile", + { + did, + server: this.partnerApiServer, + status: axiosError.response.status, + timestamp: new Date().toISOString(), + }, + ); + throw new Error("Authentication required"); + } + + if (axiosError.response?.status === 409) { + logger.warn("[Profile] Profile conflict - may already exist", { + did, + server: this.partnerApiServer, + status: 409, + timestamp: new Date().toISOString(), + }); + throw new Error("Profile already exists"); + } + } + + // Only log full errors for unexpected issues + logger.error("[Profile] Unexpected error saving profile:", { + did, + server: this.partnerApiServer, + error: error instanceof Error ? error.message : String(error), + timestamp: new Date().toISOString(), + }); + throw new Error("Failed to save profile"); + } + } + + /** + * Toggle profile location visibility + */ + private toggleProfileLocation(profileData: ProfileData): ProfileData { + const includeLocation = !profileData.includeLocation; + return { + ...profileData, + latitude: includeLocation ? profileData.latitude : 0, + longitude: includeLocation ? profileData.longitude : 0, + includeLocation, + }; + } + + /** + * Clear profile location + */ + private clearProfileLocation(profileData: ProfileData): ProfileData { + return { + ...profileData, + latitude: 0, + longitude: 0, + includeLocation: false, + }; + } + + /** + * Get default profile data + */ + private getDefaultProfile(): ProfileData { + return { + description: "", + latitude: 0, + longitude: 0, + includeLocation: false, + }; + } + + /** + * Delete user profile from the partner API + */ + private async deleteProfileFromServer(did: string): Promise<boolean> { + try { + const requestId = `profile_delete_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + logger.debug("[AccountViewView] Deleting profile:", { + requestId, + did, + }); + + // Get authentication headers + const headers = await getHeaders(did); + + const response = await this.axios.delete( + `${this.partnerApiServer}/api/partner/userProfile/${did}`, + { headers }, + ); + + logger.debug("[AccountViewView] Profile deleted successfully:", { + requestId, + status: response.status, + }); + + return true; + } catch (error: unknown) { + // Handle specific HTTP status codes cleanly to suppress console spam + if (error && typeof error === "object" && "response" in error) { + const axiosError = error as { response?: { status?: number } }; + + if (axiosError.response?.status === 404) { + logger.info( + "[Profile] Profile not found for deletion - may already be deleted", + { + did, + server: this.partnerApiServer, + status: 404, + timestamp: new Date().toISOString(), + }, + ); + return true; // Consider it successful if already deleted + } + + if ( + axiosError.response?.status === 401 || + axiosError.response?.status === 403 + ) { + logger.warn( + "[Profile] Authentication/authorization issue deleting profile", + { + did, + server: this.partnerApiServer, + status: axiosError.response.status, + timestamp: new Date().toISOString(), + }, + ); + return false; + } + } + + // Only log full errors for unexpected issues + logger.error("[Profile] Unexpected error deleting profile:", { + did, + server: this.partnerApiServer, + error: error instanceof Error ? error.message : String(error), + timestamp: new Date().toISOString(), + }); + return false; + } } } </script> diff --git a/src/views/ContactAmountsView.vue b/src/views/ContactAmountsView.vue index 233ddbce..56ee2061 100644 --- a/src/views/ContactAmountsView.vue +++ b/src/views/ContactAmountsView.vue @@ -124,7 +124,7 @@ import { NOTIFY_CONFIRMATION_RESTRICTION, } from "../constants/notifications"; import { Contact } from "../db/tables/contacts"; -import { MASTER_SETTINGS_KEY } from "../db/tables/settings"; + import { GiveSummaryRecord, GiveActionClaim } from "../interfaces"; import { AgreeActionClaim } from "../interfaces/claims"; import { @@ -223,7 +223,7 @@ export default class ContactAmountssView extends Vue { const contact = await this.$getContact(contactDid); this.contact = contact; - const settings = await this.$getSettings(MASTER_SETTINGS_KEY); + const settings = await this.$getMasterSettings(); this.activeDid = settings?.activeDid || ""; this.apiServer = settings?.apiServer || ""; diff --git a/src/views/ContactGiftingView.vue b/src/views/ContactGiftingView.vue index 91d10c9c..21adf7cf 100644 --- a/src/views/ContactGiftingView.vue +++ b/src/views/ContactGiftingView.vue @@ -17,20 +17,40 @@ <!-- Results List --> <ul class="border-t border-slate-300"> + <!-- "You" entity --> + <li v-if="shouldShowYouEntity" class="border-b border-slate-300 py-3"> + <h2 class="text-base flex gap-4 items-center"> + <span class="grow flex gap-2 items-center font-medium"> + <font-awesome icon="hand" class="text-blue-500 text-4xl shrink-0" /> + <span class="text-ellipsis overflow-hidden text-blue-500">You</span> + </span> + <span class="text-right"> + <button + type="button" + class="block w-full text-center text-sm uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md" + @click="openDialog({ did: activeDid, name: 'You' })" + > + <font-awesome icon="gift" class="fa-fw"></font-awesome> + </button> + </span> + </h2> + </li> <li class="border-b border-slate-300 py-3"> <h2 class="text-base flex gap-4 items-center"> <span class="grow flex gap-2 items-center font-medium"> <font-awesome icon="circle-question" - class="text-slate-400 text-4xl" + class="text-slate-400 text-4xl shrink-0" /> - <span class="italic text-slate-400">(Unnamed/Unknown)</span> + <span class="text-ellipsis overflow-hidden italic text-slate-500">{{ + unnamedEntityName + }}</span> </span> <span class="text-right"> <button type="button" class="block w-full text-center text-sm uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md" - @click="openDialog('Unnamed')" + @click="openDialog({ did: '', name: unnamedEntityName })" > <font-awesome icon="gift" class="fa-fw"></font-awesome> </button> @@ -43,14 +63,22 @@ class="border-b border-slate-300 py-3" > <h2 class="text-base flex gap-4 items-center"> - <span class="grow flex gap-2 items-center font-medium"> + <span + class="grow flex gap-2 items-center font-medium overflow-hidden" + > <EntityIcon :contact="contact" :icon-size="34" - class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden" + class="inline-block align-middle border border-slate-300 rounded-full overflow-hidden shrink-0" /> - <span v-if="contact.name">{{ contact.name }}</span> - <span v-else class="italic text-slate-400">(No name)</span> + <span v-if="contact.name" class="text-ellipsis overflow-hidden">{{ + contact.name + }}</span> + <span + v-else + class="text-ellipsis overflow-hidden italic text-slate-500" + >{{ contact.did }}</span + > </span> <span class="text-right"> <button @@ -72,6 +100,7 @@ :from-project-id="fromProjectId" :to-project-id="toProjectId" :is-from-project-view="isFromProjectView" + :hide-show-all="true" /> </section> </template> @@ -89,6 +118,7 @@ import { GiverReceiverInputInfo } from "../libs/util"; import { logger } from "../utils/logger"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; +import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; @Component({ components: { GiftedDialog, QuickNav, EntityIcon }, mixins: [PlatformServiceMixin], @@ -188,147 +218,151 @@ export default class ContactGiftingView extends Vue { } } - openDialog(contact?: GiverReceiverInputInfo | "Unnamed") { - if (contact === "Unnamed") { - // Special case: Handle "Unnamed" contacts for both givers and recipients - let recipient: GiverReceiverInputInfo; - let giver: GiverReceiverInputInfo | undefined; + openDialog(contact?: GiverReceiverInputInfo) { + // Determine the selected entity based on contact type + const selectedEntity = this.createEntityFromContact(contact); - if (this.stepType === "giver") { - // We're selecting a giver, so preserve the existing recipient from context - if (this.recipientEntityType === "project") { - recipient = { - did: this.recipientProjectHandleId, - name: this.recipientProjectName, - image: this.recipientProjectImage, - handleId: this.recipientProjectHandleId, - }; - } else { - // Preserve the existing recipient from context - if (this.recipientDid === this.activeDid) { - // Recipient was "You" - recipient = { did: this.activeDid, name: "You" }; - } else if (this.recipientDid) { - // Recipient was a regular contact - recipient = { - did: this.recipientDid, - name: this.recipientProjectName || "Someone", - }; - } else { - // Fallback to "You" if no recipient was previously selected - recipient = { did: this.activeDid, name: "You" }; - } - } - giver = undefined; // Will be set to "Unnamed" in GiftedDialog - } else { - // We're selecting a recipient, so recipient is "Unnamed" and giver is preserved from context - recipient = { did: "", name: "Unnamed" }; + // Create giver and recipient based on step type and selected entity + const { giver, recipient } = this.createGiverAndRecipient(selectedEntity); - // Preserve the existing giver from the context - if (this.giverEntityType === "project") { - giver = { - did: this.giverProjectHandleId, - name: this.giverProjectName, - image: this.giverProjectImage, - handleId: this.giverProjectHandleId, - }; - } else if (this.giverDid) { - giver = { - did: this.giverDid, - name: this.giverProjectName || "Someone", - }; - } else { - giver = { did: this.activeDid, name: "You" }; - } - } + // Open the dialog + (this.$refs.giftedDialog as GiftedDialog).open( + giver, + recipient, + this.offerId, + this.prompt, + this.description, + this.amountInput, + this.unitCode, + ); - (this.$refs.giftedDialog as GiftedDialog).open( - giver, - recipient, - this.offerId, - this.prompt, - this.description, - this.amountInput, - this.unitCode, - ); + // Move to Step 2 - entities are already set by the open() call + (this.$refs.giftedDialog as GiftedDialog).moveToStep2(); + } + + /** + * Creates an entity object from the contact parameter + * Uses DID-based logic to determine "You" and "Unnamed" entities + */ + private createEntityFromContact( + contact?: GiverReceiverInputInfo, + ): GiverReceiverInputInfo | undefined { + if (!contact) { + return undefined; + } - // Move to Step 2 - entities are already set by the open() call - (this.$refs.giftedDialog as GiftedDialog).moveToStep2(); + // Handle GiverReceiverInputInfo object + if (contact.did === this.activeDid) { + // If DID matches active DID, create "You" entity + return { did: this.activeDid, name: "You" }; + } else if (!contact.did || contact.did === "") { + // If DID is empty/null, create "Unnamed" entity + return { did: "", name: UNNAMED_ENTITY_NAME }; } else { - // Regular case: contact is a GiverReceiverInputInfo - let giver: GiverReceiverInputInfo; - let recipient: GiverReceiverInputInfo; + // Create a copy of the contact to avoid modifying the original + return { ...contact }; + } + } - if (this.stepType === "giver") { - // We're selecting a giver, so the contact becomes the giver - giver = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined + /** + * Creates giver and recipient objects based on step type and selected entity + */ + private createGiverAndRecipient(selectedEntity?: GiverReceiverInputInfo): { + giver: GiverReceiverInputInfo | undefined; + recipient: GiverReceiverInputInfo; + } { + if (this.stepType === "giver") { + // We're selecting a giver, so the selected entity becomes the giver + const giver = selectedEntity; + const recipient = this.createRecipientFromContext(); + return { giver, recipient }; + } else { + // We're selecting a recipient, so the selected entity becomes the recipient + const recipient = selectedEntity || { + did: "", + name: UNNAMED_ENTITY_NAME, + }; + const giver = this.createGiverFromContext(); + return { giver, recipient }; + } + } - // Preserve the existing recipient from the context - if (this.recipientEntityType === "project") { - recipient = { - did: this.recipientProjectHandleId, - name: this.recipientProjectName, - image: this.recipientProjectImage, - handleId: this.recipientProjectHandleId, - }; - } else { - // Check if the preserved recipient was "You" or a regular contact - if (this.recipientDid === this.activeDid) { - // Recipient was "You" - recipient = { did: this.activeDid, name: "You" }; - } else if (this.recipientDid) { - // Recipient was a regular contact - recipient = { - did: this.recipientDid, - name: this.recipientProjectName || "Someone", - }; - } else { - // Fallback to "Unnamed" - recipient = { did: "", name: "Unnamed" }; - } - } + /** + * Creates recipient object from context (preserves existing recipient) + */ + private createRecipientFromContext(): GiverReceiverInputInfo { + if (this.recipientEntityType === "project") { + return { + name: this.recipientProjectName, + image: this.recipientProjectImage, + handleId: this.recipientProjectHandleId, + }; + } else { + if (this.recipientDid === this.activeDid) { + return { did: this.activeDid, name: "You" }; + } else if (this.recipientDid) { + return { + did: this.recipientDid, + name: this.recipientProjectName, + }; } else { - // We're selecting a recipient, so the contact becomes the recipient - recipient = contact as GiverReceiverInputInfo; // Safe because we know contact is not "Unnamed" or undefined + return { did: "", name: UNNAMED_ENTITY_NAME }; + } + } + } - // Preserve the existing giver from the context - if (this.giverEntityType === "project") { - giver = { - did: this.giverProjectHandleId, - name: this.giverProjectName, - image: this.giverProjectImage, - handleId: this.giverProjectHandleId, - }; - } else { - // Check if the preserved giver was "You" or a regular contact - if (this.giverDid === this.activeDid) { - // Giver was "You" - giver = { did: this.activeDid, name: "You" }; - } else if (this.giverDid) { - // Giver was a regular contact - giver = { - did: this.giverDid, - name: this.giverProjectName || "Someone", - }; - } else { - // Fallback to "Unnamed" - giver = { did: "", name: "Unnamed" }; - } - } + /** + * Creates giver object from context (preserves existing giver) + */ + private createGiverFromContext(): GiverReceiverInputInfo { + if (this.giverEntityType === "project") { + return { + name: this.giverProjectName, + image: this.giverProjectImage, + handleId: this.giverProjectHandleId, + }; + } else { + if (this.giverDid === this.activeDid) { + return { did: this.activeDid, name: "You" }; + } else if (this.giverDid) { + return { + did: this.giverDid, + name: this.giverProjectName, + }; + } else { + return { did: "", name: UNNAMED_ENTITY_NAME }; } + } + } - (this.$refs.giftedDialog as GiftedDialog).open( - giver, - recipient, - this.offerId, - this.prompt, - this.description, - this.amountInput, - this.unitCode, - ); + /** + * Get the unnamed entity name constant + */ + get unnamedEntityName(): string { + return UNNAMED_ENTITY_NAME; + } - // Move to Step 2 - entities are already set by the open() call - (this.$refs.giftedDialog as GiftedDialog).moveToStep2(); + get shouldShowYouEntity(): boolean { + if (this.stepType === "giver") { + // When selecting a giver, show "You" if the current recipient is not "You" + // This prevents selecting yourself as both giver and recipient + if (this.recipientEntityType === "project") { + // If recipient is a project, we can select "You" as giver + return true; + } else { + // If recipient is a person, check if it's not "You" + return this.recipientDid !== this.activeDid; + } + } else { + // When selecting a recipient, show "You" if the current giver is not "You" + // This prevents selecting yourself as both giver and recipient + if (this.giverEntityType === "project") { + // If giver is a project, we can select "You" as recipient + return true; + } else { + // If giver is a person, check if it's not "You" + return this.giverDid !== this.activeDid; + } } } } diff --git a/src/views/ContactQRScanFullView.vue b/src/views/ContactQRScanFullView.vue index c2e7b455..bb52bea4 100644 --- a/src/views/ContactQRScanFullView.vue +++ b/src/views/ContactQRScanFullView.vue @@ -143,7 +143,7 @@ import { QR_TIMEOUT_STANDARD, QR_TIMEOUT_LONG, } from "@/constants/notifications"; -import { createNotifyHelpers } from "../utils/notify"; +import { createNotifyHelpers, NotifyFunction } from "../utils/notify"; interface QRScanResult { rawValue?: string; @@ -191,7 +191,7 @@ interface IUserNameDialog { * @since 2024 */ export default class ContactQRScanFull extends Vue { - $notify!: (notification: any, timeout?: number) => void; + $notify!: NotifyFunction; $router!: Router; // Notification helper system @@ -220,21 +220,21 @@ export default class ContactQRScanFull extends Vue { * Computed property for QR code container CSS classes */ get qrContainerClasses(): string { - return "block w-full max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto mt-4"; + return "block w-full max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto mt-4"; } /** * Computed property for camera frame CSS classes */ get cameraFrameClasses(): string { - return "relative w-full max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto border border-dashed border-white mt-8 aspect-square"; + return "relative w-full max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto border border-dashed border-white mt-8 aspect-square"; } /** * Computed property for main content container CSS classes */ get mainContentClasses(): string { - return "p-6 bg-white w-full max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto"; + return "p-6 bg-white w-full max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto"; } /** diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue index c23b7376..bdf86a84 100644 --- a/src/views/ContactQRScanShowView.vue +++ b/src/views/ContactQRScanShowView.vue @@ -140,7 +140,7 @@ import { AxiosError } from "axios"; import { Buffer } from "buffer/"; import QRCodeVue3 from "qr-code-generator-vue3"; import { Component, Vue } from "vue-facing-decorator"; -import { useClipboard } from "@vueuse/core"; + import { QrcodeStream } from "vue-qrcode-reader"; import QuickNav from "../components/QuickNav.vue"; @@ -183,8 +183,6 @@ import { NOTIFY_QR_PROCESSING_ERROR, createQRContactAddedMessage, createQRRegistrationSuccessMessage, - QR_TIMEOUT_SHORT, - QR_TIMEOUT_MEDIUM, QR_TIMEOUT_STANDARD, QR_TIMEOUT_LONG, } from "@/constants/notifications"; @@ -259,11 +257,11 @@ export default class ContactQRScanShow extends Vue { } get qrCodeContainerClasses(): string { - return "block w-[90vw] max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto my-4"; + return "block w-[90vw] max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto my-4"; } get scannerContainerClasses(): string { - return "relative aspect-square overflow-hidden bg-slate-800 w-[90vw] max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto"; + return "relative aspect-square overflow-hidden bg-slate-800 w-[90vw] max-w-[calc((100vh-max(env(safe-area-inset-top),var(--safe-area-inset-top,0px))-max(env(safe-area-inset-bottom),var(--safe-area-inset-bottom,0px)))*0.4)] mx-auto"; } get statusMessageClasses(): string { @@ -544,11 +542,7 @@ export default class ContactQRScanShow extends Vue { did: contact.did, name: contact.name, }); - this.notify.toast( - "Submitted", - NOTIFY_QR_REGISTRATION_SUBMITTED.message, - QR_TIMEOUT_SHORT, - ); + this.notify.toast("Submitted", NOTIFY_QR_REGISTRATION_SUBMITTED.message); try { const regResult = await register( @@ -624,18 +618,15 @@ export default class ContactQRScanShow extends Vue { ); // Copy the URL to clipboard - useClipboard() - .copy(jwtUrl) - .then(() => { - this.notify.toast( - "Copied", - NOTIFY_QR_URL_COPIED.message, - QR_TIMEOUT_MEDIUM, - ); - }); + const { copyToClipboard } = await import("../services/ClipboardService"); + await copyToClipboard(jwtUrl); + this.notify.toast( + NOTIFY_QR_URL_COPIED.title, + NOTIFY_QR_URL_COPIED.message, + ); } catch (error) { - logger.error("Failed to generate contact URL:", error); - this.notify.error("Failed to generate contact URL. Please try again."); + this.$logAndConsole(`Error copying URL to clipboard: ${error}`, true); + this.notify.error("Failed to copy URL to clipboard."); } } @@ -643,13 +634,16 @@ export default class ContactQRScanShow extends Vue { this.notify.info(NOTIFY_QR_CODE_HELP.message, QR_TIMEOUT_LONG); } - onCopyDidToClipboard() { + async onCopyDidToClipboard() { //this.onScanDetect([{ rawValue: this.qrValue }]); // good for testing - useClipboard() - .copy(this.activeDid) - .then(() => { - this.notify.info(NOTIFY_QR_DID_COPIED.message, QR_TIMEOUT_LONG); - }); + try { + const { copyToClipboard } = await import("../services/ClipboardService"); + await copyToClipboard(this.activeDid); + this.notify.info(NOTIFY_QR_DID_COPIED.message, QR_TIMEOUT_LONG); + } catch (error) { + this.$logAndConsole(`Error copying DID to clipboard: ${error}`, true); + this.notify.error("Failed to copy DID to clipboard."); + } } openUserNameDialog() { @@ -714,8 +708,16 @@ export default class ContactQRScanShow extends Vue { // Add new contact // @ts-expect-error because we're just using the value to store to the DB + // eslint-disable-next-line @typescript-eslint/no-explicit-any contact.contactMethods = JSON.stringify( - (this as any)._parseJsonField(contact.contactMethods, []), + ( + this as { + _parseJsonField: ( + value: unknown, + defaultValue: unknown[], + ) => unknown[]; + } + )._parseJsonField(contact.contactMethods, []), ); await this.$insertContact(contact); @@ -737,7 +739,6 @@ export default class ContactQRScanShow extends Vue { ) { setTimeout(() => { this.notify.confirm( - "Register", "Do you want to register them?", { onCancel: async (stopAsking?: boolean) => { diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 23208aae..2ed7611f 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -130,10 +130,9 @@ import { JWTPayload } from "did-jwt"; import * as R from "ramda"; import { Component, Vue } from "vue-facing-decorator"; import { RouteLocationNormalizedLoaded, Router } from "vue-router"; -import { useClipboard } from "@vueuse/core"; -import { Capacitor } from "@capacitor/core"; import QuickNav from "../components/QuickNav.vue"; +import { copyToClipboard } from "../services/ClipboardService"; import EntityIcon from "../components/EntityIcon.vue"; import GiftedDialog from "../components/GiftedDialog.vue"; import OfferDialog from "../components/OfferDialog.vue"; @@ -165,13 +164,18 @@ import { GiveSummaryRecord } from "@/interfaces/records"; import { UserInfo } from "@/interfaces/common"; import { VerifiableCredential } from "@/interfaces/claims-result"; import * as libsUtil from "../libs/util"; -import { generateSaveAndActivateIdentity } from "../libs/util"; +import { + generateSaveAndActivateIdentity, + contactsToExportJson, +} from "../libs/util"; import { logger } from "../utils/logger"; // No longer needed - using PlatformServiceMixin methods // import { PlatformServiceFactory } from "@/services/PlatformServiceFactory"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; +import { isDatabaseError } from "@/interfaces/common"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { APP_SERVER } from "@/constants/app"; +import { QRNavigationService } from "@/services/QRNavigationService"; import { NOTIFY_CONTACT_NO_INFO, NOTIFY_CONTACTS_ADD_ERROR, @@ -197,6 +201,7 @@ import { NOTIFY_REGISTRATION_ERROR_FALLBACK, NOTIFY_REGISTRATION_ERROR_GENERIC, NOTIFY_VISIBILITY_ERROR_FALLBACK, + NOTIFY_EXPORT_DATA_PROMPT, getRegisterPersonSuccessMessage, getVisibilitySuccessMessage, getGivesRetrievalErrorMessage, @@ -376,7 +381,11 @@ export default class ContactsView extends Vue { "", async (name) => { await this.addContact({ - did: (registration.vc.credentialSubject.agent as any).identifier, + did: ( + registration.vc.credentialSubject.agent as { + identifier: string; + } + ).identifier, name: name, registered: true, }); @@ -387,7 +396,11 @@ export default class ContactsView extends Vue { async () => { // on cancel, will still add the contact await this.addContact({ - did: (registration.vc.credentialSubject.agent as any).identifier, + did: ( + registration.vc.credentialSubject.agent as { + identifier: string; + } + ).identifier, name: "(person who invited you)", registered: true, }); @@ -396,8 +409,7 @@ export default class ContactsView extends Vue { this.showOnboardingInfo(); }, ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (error: any) { + } catch (error: unknown) { const fullError = "Error redeeming invite: " + errorStringForLog(error); this.$logAndConsole(fullError, true); let message = "Got an error sending the invite."; @@ -784,6 +796,9 @@ export default class ContactsView extends Vue { // Show success notification this.notify.success(addedMessage); + + // Show export data prompt after successful contact addition + await this.showExportDataPrompt(); } catch (err) { this.handleContactAddError(err); } @@ -881,20 +896,21 @@ export default class ContactsView extends Vue { /** * Handle errors during contact addition */ - private handleContactAddError(err: any): void { + private handleContactAddError(err: unknown): void { const fullError = "Error when adding contact to storage: " + errorStringForLog(err); this.$logAndConsole(fullError, true); let message = NOTIFY_CONTACT_IMPORT_ERROR.message; - if ( - (err as any).message?.indexOf("Key already exists in the object store.") > - -1 - ) { - message = NOTIFY_CONTACT_IMPORT_CONFLICT.message; - } - if ((err as any).name === "ConstraintError") { - message += " " + NOTIFY_CONTACT_IMPORT_CONSTRAINT.message; + + // Use type-safe error checking with our new type guards + if (isDatabaseError(err)) { + if (err.message.includes("Key already exists in the object store")) { + message = NOTIFY_CONTACT_IMPORT_CONFLICT.message; + } + if (err.name === "ConstraintError") { + message += " " + NOTIFY_CONTACT_IMPORT_CONSTRAINT.message; + } } this.notify.error(message, TIMEOUTS.LONG); @@ -1175,12 +1191,14 @@ export default class ContactsView extends Vue { }); // Use production URL for sharing to avoid localhost issues in development const contactsJwtUrl = `${APP_SERVER}/deep-link/contact-import/${contactsJwt}`; - useClipboard() - .copy(contactsJwtUrl) - .then(() => { - // Use notification helper - this.notify.copied(NOTIFY_CONTACT_LINK_COPIED.message); - }); + try { + await copyToClipboard(contactsJwtUrl); + // Use notification helper + this.notify.copied(NOTIFY_CONTACT_LINK_COPIED.message); + } catch (error) { + this.$logAndConsole(`Error copying to clipboard: ${error}`, true); + this.notify.error("Failed to copy to clipboard. Please try again."); + } } private showCopySelectionsInfo() { @@ -1246,19 +1264,76 @@ export default class ContactsView extends Vue { /** * Handle QR code button click - route to appropriate scanner - * Uses native scanner on mobile platforms, web scanner otherwise + * Uses QRNavigationService to determine scanner type and route */ - public handleQRCodeClick() { this.$logAndConsole( "[ContactsView] handleQRCodeClick method called", false, ); - if (Capacitor.isNativePlatform()) { - this.$router.push({ name: "contact-qr-scan-full" }); - } else { - this.$router.push({ name: "contact-qr" }); + const qrNavigationService = QRNavigationService.getInstance(); + const route = qrNavigationService.getQRScannerRoute(); + + this.$router.push(route); + } + + /** + * Show export data prompt after adding a contact + * Prompts user to export their contact data as a backup + */ + private async showExportDataPrompt(): Promise<void> { + setTimeout(() => { + this.$notify( + { + group: "modal", + type: "confirm", + title: NOTIFY_EXPORT_DATA_PROMPT.title, + text: NOTIFY_EXPORT_DATA_PROMPT.message, + onYes: async () => { + await this.exportContactData(); + }, + yesText: "Export Data", + onNo: async () => { + // User chose not to export - no action needed + }, + noText: "Not Now", + }, + -1, + ); + }, 1000); // Small delay to ensure success notification is shown first + } + + /** + * Export contact data to JSON file + * Uses platform service to handle platform-specific export logic + */ + private async exportContactData(): Promise<void> { + // Note that similar code is in DataExportSection.vue exportDatabase() + try { + // Fetch all contacts from database + const allContacts = await this.$contacts(); + + // Convert contacts to export format + const exportData = contactsToExportJson(allContacts); + const jsonStr = JSON.stringify(exportData, null, 2); + + // Generate filename with current date + const today = new Date(); + const dateString = today.toISOString().split("T")[0]; // YYYY-MM-DD format + const fileName = `timesafari-backup-contacts-${dateString}.json`; + + // Use platform service to handle export + await this.platformService.writeAndShareFile(fileName, jsonStr); + + this.notify.success( + "Contact export completed successfully. Check your downloads or share dialog.", + ); + } catch (error) { + logger.error("Export Error:", error); + this.notify.error( + `There was an error exporting the data: ${error instanceof Error ? error.message : "Unknown error"}`, + ); } } } diff --git a/src/views/DIDView.vue b/src/views/DIDView.vue index 4294ec4f..4a793d0e 100644 --- a/src/views/DIDView.vue +++ b/src/views/DIDView.vue @@ -71,22 +71,22 @@ contactFromDid?.seesMe && contactFromDid.did !== activeDid " class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md" - title="They can see you" + title="They can see your activity" @click="confirmSetVisibility(contactFromDid, false)" > - <font-awesome icon="eye" class="fa-fw" /> <font-awesome icon="arrow-up" class="fa-fw" /> + <font-awesome icon="eye" class="fa-fw" /> </button> <button v-else-if=" !contactFromDid?.seesMe && contactFromDid?.did !== activeDid " class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md" - title="They cannot see you" + title="They cannot see your activity" @click="confirmSetVisibility(contactFromDid, true)" > - <font-awesome icon="eye-slash" class="fa-fw" /> <font-awesome icon="arrow-up" class="fa-fw" /> + <font-awesome icon="eye-slash" class="fa-fw" /> </button> <button @@ -95,11 +95,11 @@ contactFromDid.did !== activeDid " class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md" - title="I view their content" + title="You watch their activity" @click="confirmViewContent(contactFromDid, false)" > - <font-awesome icon="eye" class="fa-fw" /> <font-awesome icon="arrow-down" class="fa-fw" /> + <font-awesome icon="eye" class="fa-fw" /> </button> <button v-else-if=" @@ -107,11 +107,11 @@ contactFromDid?.did !== activeDid " class="text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md" - title="I do not view their content" + title="You do not watch their activity" @click="confirmViewContent(contactFromDid, true)" > - <font-awesome icon="eye-slash" class="fa-fw" /> <font-awesome icon="arrow-down" class="fa-fw" /> + <font-awesome icon="eye-slash" class="fa-fw" /> </button> <button @@ -273,6 +273,7 @@ import { didInfoForContact, displayAmount, getHeaders, + isDid, register, setVisibilityUtil, } from "../libs/endorserServer"; @@ -289,7 +290,9 @@ import { NOTIFY_REGISTRATION_ERROR, NOTIFY_SERVER_ACCESS_ERROR, NOTIFY_NO_IDENTITY_ERROR, + NOTIFY_CONTACT_INVALID_DID, } from "@/constants/notifications"; +import { THAT_UNNAMED_PERSON } from "@/constants/entities"; /** * DIDView Component @@ -379,22 +382,29 @@ export default class DIDView extends Vue { /** * Determines which DID to display based on URL parameters - * Falls back to active DID if no parameter provided + * Validates DID format and shows error for invalid DIDs */ private async determineDIDToDisplay() { const pathParam = window.location.pathname.substring("/did/".length); let showDid = pathParam; if (!showDid) { + // No DID provided in URL, use active DID showDid = this.activeDid; - if (showDid) { - this.notifyDefaultToActiveDID(); + this.notifyDefaultToActiveDID(); + } else { + // DID provided in URL, validate it + const decodedDid = decodeURIComponent(showDid); + if (!isDid(decodedDid)) { + // Invalid DID format - show error and redirect + this.notify.error(NOTIFY_CONTACT_INVALID_DID.message, TIMEOUTS.LONG); + this.$router.push({ name: "home" }); + return; } + showDid = decodedDid; } - if (showDid) { - this.viewingDid = decodeURIComponent(showDid); - } + this.viewingDid = showDid; } /** @@ -490,6 +500,8 @@ export default class DIDView extends Vue { message += " Note that they can see your activity, so if you want to hide your activity from them then you should do that first."; } + message += + " Note that this will also remove anyone with the same DID underneath."; this.notify.confirm(message, async () => { await this.deleteContact(contact); }); @@ -549,7 +561,7 @@ export default class DIDView extends Vue { contact.registered = true; await this.$updateContact(contact.did, { registered: true }); - const name = contact.name || "That unnamed person"; + const name = contact.name || THAT_UNNAMED_PERSON; this.notify.success( `${name} ${NOTIFY_REGISTRATION_SUCCESS.message}`, TIMEOUTS.LONG, @@ -829,26 +841,3 @@ export default class DIDView extends Vue { } } </script> - -<style> -.dialog-overlay { - z-index: 50; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - padding: 1.5rem; -} -.dialog { - background-color: white; - padding: 1rem; - border-radius: 0.5rem; - width: 100%; - max-width: 500px; -} -</style> diff --git a/src/views/DatabaseMigration.vue b/src/views/DatabaseMigration.vue index a2edc1c9..ec5e2eef 100644 --- a/src/views/DatabaseMigration.vue +++ b/src/views/DatabaseMigration.vue @@ -91,17 +91,12 @@ class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="downloadAccount" > - <IconRenderer + <font-awesome v-if="isLoading" - icon-name="spinner" - svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" - fill="currentColor" - /> - <IconRenderer - v-else - icon-name="chart" - svg-class="-ml-1 mr-3 h-5 w-5" + icon="spinner" + class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" /> + <font-awesome v-else icon="chart-line" class="-ml-1 mr-3 h-5 w-5" /> Show Account Seed </button> @@ -110,17 +105,12 @@ class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="downloadSettingsContacts" > - <IconRenderer + <font-awesome v-if="isLoading" - icon-name="spinner" - svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" - fill="currentColor" - /> - <IconRenderer - v-else - icon-name="chart" - svg-class="-ml-1 mr-3 h-5 w-5" + icon="spinner" + class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" /> + <font-awesome v-else icon="chart-line" class="-ml-1 mr-3 h-5 w-5" /> Download Settings & Contacts </button> @@ -143,17 +133,12 @@ class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="compareDatabases" > - <IconRenderer + <font-awesome v-if="isLoading" - icon-name="spinner" - svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" - fill="currentColor" - /> - <IconRenderer - v-else - icon-name="chart" - svg-class="-ml-1 mr-3 h-5 w-5" + icon="spinner" + class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" /> + <font-awesome v-else icon="chart-line" class="-ml-1 mr-3 h-5 w-5" /> Compare Databases </button> @@ -162,17 +147,12 @@ class="inline-flex items-center px-6 py-3 border border-transparent text-base font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="migrateAll" > - <IconRenderer + <font-awesome v-if="isLoading" - icon-name="spinner" - svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" - fill="currentColor" - /> - <IconRenderer - v-else - icon-name="check" - svg-class="-ml-1 mr-3 h-5 w-5" + icon="spinner" + class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" /> + <font-awesome v-else icon="check" class="-ml-1 mr-3 h-5 w-5" /> Migrate All </button> @@ -185,10 +165,9 @@ > <div class="flex"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="warning" - svg-class="h-5 w-5 text-red-400" - fill="currentColor" + <font-awesome + icon="triangle-exclamation" + class="h-5 w-5 text-red-400" /> </div> <div class="ml-3"> @@ -207,10 +186,9 @@ > <div class="flex"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="warning" - svg-class="h-5 w-5 text-red-400" - fill="currentColor" + <font-awesome + icon="triangle-exclamation" + class="h-5 w-5 text-red-400" /> </div> <div class="ml-3"> @@ -229,10 +207,7 @@ > <div class="flex"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="check" - svg-class="h-5 w-5 text-green-400" - /> + <font-awesome icon="check" class="h-5 w-5 text-green-400" /> </div> <div class="ml-3"> <h3 class="text-sm font-medium text-green-800">Success</h3> @@ -249,7 +224,7 @@ class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="exportComparison" > - <IconRenderer icon-name="download" svg-class="-ml-1 mr-3 h-5 w-5" /> + <font-awesome icon="download" class="-ml-1 mr-3 h-5 w-5" /> Export Comparison </button> @@ -258,17 +233,12 @@ class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="displayDatabases" > - <IconRenderer + <font-awesome v-if="isLoading" - icon-name="spinner" - svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" - fill="currentColor" - /> - <IconRenderer - v-else - icon-name="chart" - svg-class="-ml-1 mr-3 h-5 w-5" + icon="spinner" + class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" /> + <font-awesome v-else icon="chart-line" class="-ml-1 mr-3 h-5 w-5" /> Show Previous Data </button> @@ -277,7 +247,7 @@ class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-orange-600 hover:bg-orange-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="migrateAccounts" > - <IconRenderer icon-name="lock" svg-class="-ml-1 mr-3 h-5 w-5" /> + <font-awesome icon="lock" class="-ml-1 mr-3 h-5 w-5" /> Migrate Accounts </button> @@ -286,7 +256,7 @@ class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-purple-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="migrateSettings" > - <IconRenderer icon-name="settings" svg-class="-ml-1 mr-3 h-5 w-5" /> + <font-awesome icon="gear" class="-ml-1 mr-3 h-5 w-5" /> Migrate Settings </button> @@ -295,7 +265,7 @@ class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 disabled:opacity-50 disabled:cursor-not-allowed" @click="migrateContacts" > - <IconRenderer icon-name="plus" svg-class="-ml-1 mr-3 h-5 w-5" /> + <font-awesome icon="plus" class="-ml-1 mr-3 h-5 w-5" /> Migrate Contacts </button> </div> @@ -316,11 +286,7 @@ > <div class="flex"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="info" - svg-class="h-5 w-5 text-blue-400" - fill="currentColor" - /> + <font-awesome icon="info" class="h-5 w-5 text-blue-400" /> </div> <div class="ml-3"> <h3 class="text-sm font-medium text-blue-800"> @@ -357,10 +323,9 @@ <div class="inline-flex items-center px-4 py-2 font-semibold leading-6 text-sm shadow rounded-md text-white bg-blue-500 hover:bg-blue-400 transition ease-in-out duration-150 cursor-not-allowed" > - <IconRenderer - icon-name="spinner" - svg-class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" - fill="currentColor" + <font-awesome + icon="spinner" + class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" /> {{ loadingMessage }} </div> @@ -375,10 +340,7 @@ <div class="p-5"> <div class="flex items-center"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="lock" - svg-class="h-6 w-6 text-orange-600" - /> + <font-awesome icon="lock" class="h-6 w-6 text-orange-600" /> </div> <div class="ml-5 w-0 flex-1"> <dl> @@ -398,10 +360,7 @@ <div class="p-5"> <div class="flex items-center"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="check" - svg-class="h-6 w-6 text-teal-600" - /> + <font-awesome icon="check" class="h-6 w-6 text-teal-600" /> </div> <div class="ml-5 w-0 flex-1"> <dl> @@ -422,10 +381,7 @@ <div class="p-5"> <div class="flex items-center"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="settings" - svg-class="h-6 w-6 text-purple-600" - /> + <font-awesome icon="gear" class="h-6 w-6 text-purple-600" /> </div> <div class="ml-5 w-0 flex-1"> <dl> @@ -445,10 +401,7 @@ <div class="p-5"> <div class="flex items-center"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="check" - svg-class="h-6 w-6 text-indigo-600" - /> + <font-awesome icon="check" class="h-6 w-6 text-indigo-600" /> </div> <div class="ml-5 w-0 flex-1"> <dl> @@ -469,9 +422,9 @@ <div class="p-5"> <div class="flex items-center"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="chart" - svg-class="h-6 w-6 text-blue-600" + <font-awesome + icon="chart-line" + class="h-6 w-6 text-blue-600" /> </div> <div class="ml-5 w-0 flex-1"> @@ -492,10 +445,7 @@ <div class="p-5"> <div class="flex items-center"> <div class="flex-shrink-0"> - <IconRenderer - icon-name="check" - svg-class="h-6 w-6 text-green-600" - /> + <font-awesome icon="check" class="h-6 w-6 text-green-600" /> </div> <div class="ml-5 w-0 flex-1"> <dl> @@ -526,9 +476,9 @@ class="flex items-center justify-between p-3 bg-blue-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="plusCircle" - svg-class="h-5 w-5 text-blue-600 mr-2" + <font-awesome + icon="circle-plus" + class="h-5 w-5 text-blue-600 mr-2" /> <span class="text-sm font-medium text-blue-900">Add</span> </div> @@ -541,9 +491,9 @@ class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="check" - svg-class="h-5 w-5 text-yellow-600 mr-2" + <font-awesome + icon="check" + class="h-5 w-5 text-yellow-600 mr-2" /> <span class="text-sm font-medium text-yellow-900" >Unmodified</span @@ -558,9 +508,9 @@ class="flex items-center justify-between p-3 bg-red-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="trash" - svg-class="h-5 w-5 text-red-600 mr-2" + <font-awesome + icon="trash-can" + class="h-5 w-5 text-red-600 mr-2" /> <span class="text-sm font-medium text-red-900">Keep</span> </div> @@ -677,9 +627,9 @@ class="flex items-center justify-between p-3 bg-blue-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="plusCircle" - svg-class="h-5 w-5 text-blue-600 mr-2" + <font-awesome + icon="circle-plus" + class="h-5 w-5 text-blue-600 mr-2" /> <span class="text-sm font-medium text-blue-900">Add</span> </div> @@ -692,9 +642,9 @@ class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="edit" - svg-class="h-5 w-5 text-yellow-600 mr-2" + <font-awesome + icon="pen" + class="h-5 w-5 text-yellow-600 mr-2" /> <span class="text-sm font-medium text-yellow-900" >Modify</span @@ -709,9 +659,9 @@ class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="check" - svg-class="h-5 w-5 text-yellow-600 mr-2" + <font-awesome + icon="check" + class="h-5 w-5 text-yellow-600 mr-2" /> <span class="text-sm font-medium text-yellow-900" >Unmodified</span @@ -726,9 +676,9 @@ class="flex items-center justify-between p-3 bg-red-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="trash" - svg-class="h-5 w-5 text-red-600 mr-2" + <font-awesome + icon="trash-can" + class="h-5 w-5 text-red-600 mr-2" /> <span class="text-sm font-medium text-red-900">Keep</span> </div> @@ -868,9 +818,9 @@ class="flex items-center justify-between p-3 bg-blue-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="plusCircle" - svg-class="h-5 w-5 text-blue-600 mr-2" + <font-awesome + icon="circle-plus" + class="h-5 w-5 text-blue-600 mr-2" /> <span class="text-sm font-medium text-blue-900">Add</span> </div> @@ -883,9 +833,9 @@ class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="edit" - svg-class="h-5 w-5 text-yellow-600 mr-2" + <font-awesome + icon="pen" + class="h-5 w-5 text-yellow-600 mr-2" /> <span class="text-sm font-medium text-yellow-900" >Modify</span @@ -900,9 +850,9 @@ class="flex items-center justify-between p-3 bg-yellow-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="check" - svg-class="h-5 w-5 text-yellow-600 mr-2" + <font-awesome + icon="check" + class="h-5 w-5 text-yellow-600 mr-2" /> <span class="text-sm font-medium text-yellow-900" >Unmodified</span @@ -917,9 +867,9 @@ class="flex items-center justify-between p-3 bg-red-50 rounded-lg" > <div class="flex items-center"> - <IconRenderer - icon-name="trash" - svg-class="h-5 w-5 text-red-600 mr-2" + <font-awesome + icon="trash-can" + class="h-5 w-5 text-red-600 mr-2" /> <span class="text-sm font-medium text-red-900">Keep</span> </div> @@ -1067,7 +1017,6 @@ import { Component, Vue } from "vue-facing-decorator"; import { useClipboard } from "@vueuse/core"; import { Router } from "vue-router"; -import IconRenderer from "../components/IconRenderer.vue"; import { compareDatabases, migrateSettings, @@ -1104,9 +1053,6 @@ import { logger } from "../utils/logger"; */ @Component({ name: "DatabaseMigration", - components: { - IconRenderer, - }, }) export default class DatabaseMigration extends Vue { $router!: Router; diff --git a/src/views/DeepLinkErrorView.vue b/src/views/DeepLinkErrorView.vue index 6decd859..803b1d5e 100644 --- a/src/views/DeepLinkErrorView.vue +++ b/src/views/DeepLinkErrorView.vue @@ -1,6 +1,6 @@ <template> - <div class="deep-link-error"> - <div class="safe-area-spacer"></div> + <!-- CONTENT --> + <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <h1>Invalid Deep Link</h1> <div class="error-details"> <div class="error-message"> @@ -39,7 +39,7 @@ </li> </ul> </div> - </div> + </section> </template> <script setup lang="ts"> @@ -47,7 +47,7 @@ import { computed, onMounted } from "vue"; import { useRoute, useRouter } from "vue-router"; import { VALID_DEEP_LINK_ROUTES, - deepLinkSchemas, + deepLinkPathSchemas, } from "../interfaces/deepLinks"; import { logConsoleAndDb } from "../db/databaseUtil"; import { logger } from "../utils/logger"; @@ -56,7 +56,7 @@ const route = useRoute(); const router = useRouter(); // an object with the route as the key and the first param name as the value const deepLinkSchemaKeys = Object.fromEntries( - Object.entries(deepLinkSchemas).map(([route, schema]) => { + Object.entries(deepLinkPathSchemas).map(([route, schema]) => { const param = Object.keys(schema.shape)[0]; return [route, param]; }), @@ -114,18 +114,6 @@ onMounted(() => { </script> <style scoped> -.deep-link-error { - padding-top: 60px; - padding-left: 20px; - padding-right: 20px; - max-width: 600px; - margin: 0 auto; -} - -.safe-area-spacer { - height: env(safe-area-inset-top); -} - h1 { color: #ff4444; margin-bottom: 24px; diff --git a/src/views/DeepLinkRedirectView.vue b/src/views/DeepLinkRedirectView.vue index 8d2cb08c..a4a88572 100644 --- a/src/views/DeepLinkRedirectView.vue +++ b/src/views/DeepLinkRedirectView.vue @@ -1,96 +1,88 @@ <template> <!-- CONTENT --> - <section id="Content" class="relative w-[100vw] h-[100vh]"> - <div - class="p-6 bg-white w-full max-w-[calc((100vh-env(safe-area-inset-top)-env(safe-area-inset-bottom))*0.4)] mx-auto" - > - <div class="mb-4"> - <h1 class="text-xl text-center font-semibold relative mb-4"> - Redirecting to Time Safari - </h1> - - <div v-if="destinationUrl" class="space-y-4"> - <!-- Platform-specific messaging --> - <div class="text-center text-gray-600 mb-4"> - <p v-if="isMobile"> - {{ - isIOS - ? "Opening Time Safari app on your iPhone..." - : "Opening Time Safari app on your Android device..." - }} - </p> - <p v-else>Opening Time Safari app...</p> - <p class="text-sm mt-2"> - <span v-if="isMobile" - >If the app doesn't open automatically, use one of these - options:</span - > - <span v-else>Choose how you'd like to open this link:</span> - </p> - </div> - - <!-- Deep Link Button --> - <div class="text-center"> - <a - :href="deepLinkUrl || '#'" - class="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors" - @click="handleDeepLinkClick" + <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> + <div class="mb-4"> + <h1 class="text-2xl text-center font-semibold relative px-7"> + Redirecting to Time Safari + </h1> + + <div v-if="destinationUrl" class="space-y-4"> + <!-- Platform-specific messaging --> + <div class="text-center text-gray-600 mb-4"> + <p v-if="isMobile"> + {{ + isIOS + ? "Opening Time Safari app on your iPhone..." + : "Opening Time Safari app on your Android device..." + }} + </p> + <p v-else>Opening Time Safari app...</p> + <p class="text-sm mt-2"> + <span v-if="isMobile" + >If the app doesn't open automatically, use one of these + options:</span > - <span v-if="isMobile">Open in Time Safari App</span> - <span v-else>Try Opening in Time Safari App</span> - </a> - </div> - - <!-- Web Fallback Link --> - <div class="text-center"> - <a - :href="webUrl || '#'" - target="_blank" - class="inline-block bg-gray-600 text-white px-6 py-3 rounded-lg font-medium hover:bg-gray-700 transition-colors" - @click="handleWebFallbackClick" - > - <span v-if="isMobile">Open in Web Browser Instead</span> - <span v-else>Open in Web Browser</span> - </a> - </div> - - <!-- Manual Instructions --> - <div class="text-center text-sm text-gray-500 mt-4"> - <p v-if="isMobile"> - Or manually open: - <code class="bg-gray-100 px-2 py-1 rounded">{{ - deepLinkUrl - }}</code> - </p> - <p v-else> - If you have the Time Safari app installed, you can also copy this - link: - <code class="bg-gray-100 px-2 py-1 rounded">{{ - deepLinkUrl - }}</code> - </p> - </div> - - <!-- Platform info for debugging --> - <div - v-if="isDevelopment" - class="text-center text-xs text-gray-400 mt-4" + <span v-else>Choose how you'd like to open this link:</span> + </p> + </div> + + <!-- Deep Link Button --> + <div class="text-center"> + <a + :href="deepLinkUrl || '#'" + class="inline-block bg-blue-600 text-white px-6 py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors" + @click="handleDeepLinkClick" + > + <span v-if="isMobile">Open in Time Safari App</span> + <span v-else>Try Opening in Time Safari App</span> + </a> + </div> + + <!-- Web Fallback Link --> + <div class="text-center"> + <a + :href="webUrl || '#'" + target="_blank" + class="inline-block bg-gray-600 text-white px-6 py-3 rounded-lg font-medium hover:bg-gray-700 transition-colors" + @click="handleWebFallbackClick" > - <p> - Platform: {{ isMobile ? (isIOS ? "iOS" : "Android") : "Desktop" }} - </p> - <p>User Agent: {{ userAgent.substring(0, 50) }}...</p> - </div> + <span v-if="isMobile">Open in Web Browser Instead</span> + <span v-else>Open in Web Browser</span> + </a> </div> - <div v-else-if="pageError" class="text-center text-red-500 mb-4"> - {{ pageError }} + <!-- Manual Instructions --> + <div class="text-center text-sm text-gray-500 mt-4"> + <p v-if="isMobile"> + Or manually open: + <code class="bg-gray-100 px-2 py-1 rounded">{{ deepLinkUrl }}</code> + </p> + <p v-else> + If you have the Time Safari app installed, you can also copy this + link: + <code class="bg-gray-100 px-2 py-1 rounded">{{ deepLinkUrl }}</code> + </p> </div> - <div v-else class="text-center text-gray-600"> - <p>Processing redirect...</p> + <!-- Platform info for debugging --> + <div + v-if="isDevelopment" + class="text-center text-xs text-gray-400 mt-4" + > + <p> + Platform: {{ isMobile ? (isIOS ? "iOS" : "Android") : "Desktop" }} + </p> + <p>User Agent: {{ userAgent.substring(0, 50) }}...</p> </div> </div> + + <div v-else-if="pageError" class="text-center text-red-500 mb-4"> + {{ pageError }} + </div> + + <div v-else class="text-center text-gray-600"> + <p>Processing redirect...</p> + </div> </div> </section> </template> diff --git a/src/views/DiscoverView.vue b/src/views/DiscoverView.vue index 452120fb..2a8879e0 100644 --- a/src/views/DiscoverView.vue +++ b/src/views/DiscoverView.vue @@ -233,7 +233,7 @@ <div class="grow"> <h2 class="text-base font-semibold"> - {{ project.name || "Unnamed Project" }} + {{ project.name || unnamedProject }} </h2> <div class="text-sm"> <font-awesome @@ -340,6 +340,7 @@ import { NOTIFY_DISCOVER_LOCAL_SEARCH_ERROR, NOTIFY_DISCOVER_MAP_SEARCH_ERROR, } from "@/constants/notifications"; +import { UNNAMED_PROJECT } from "@/constants/entities"; interface Tile { indexLat: number; indexLon: number; @@ -370,6 +371,13 @@ export default class DiscoverView extends Vue { notify!: ReturnType<typeof createNotifyHelpers>; + /** + * Get the unnamed project constant + */ + get unnamedProject(): string { + return UNNAMED_PROJECT; + } + activeDid = ""; allContacts: Array<Contact> = []; allMyDids: Array<string> = []; @@ -458,7 +466,9 @@ export default class DiscoverView extends Vue { if (this.isLocalActive) { await this.searchLocal(); } else if (this.isMappedActive) { - const mapRef = this.$refs.projectMap as any; + const mapRef = this.$refs.projectMap as { + leafletObject: L.Map; + }; this.requestTiles(mapRef.leafletObject); // not ideal because I found this from experimentation, not documentation } else { await this.searchAll(); @@ -518,11 +528,11 @@ export default class DiscoverView extends Vue { throw JSON.stringify(results); } } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (e: any) { + } catch (e: unknown) { logger.error("Error with search all: " + errorStringForLog(e)); this.notify.error( - e.userMessage || NOTIFY_DISCOVER_SEARCH_ERROR.message, + (e as { userMessage?: string })?.userMessage || + NOTIFY_DISCOVER_SEARCH_ERROR.message, TIMEOUTS.LONG, ); } finally { diff --git a/src/views/HelpView.vue b/src/views/HelpView.vue index 02106169..98bd7147 100644 --- a/src/views/HelpView.vue +++ b/src/views/HelpView.vue @@ -18,6 +18,7 @@ <!-- Heading --> <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> Help + <span class="text-xs text-gray-500">{{ package.version }}</span> </h1> </div> @@ -199,7 +200,7 @@ </p> <p> Then you can record your appreciation for... whatever: select any contact on the home page - (or "Unnamed") and send it. The main goal is to record what people + (or "{{ unnamedEntityName }}") and send it. The main goal is to record what people have given you, to grow giving economies. You can also record your own ideas for projects. Each claim is recorded on a custom ledger. @@ -318,8 +319,9 @@ <ul class="list-disc list-outside ml-4"> <li> Go to Your Identity <font-awesome icon="circle-user" class="fa-fw" /> page, - click Advanced, and follow the instructions for the Contacts & Settings Database "Import". - Beware that this will erase your existing contact & settings. + click Advanced, and follow the instructions to "Import Contacts". + (There is currently no way to import other settings, so you'll have to recreate + by hand your search area, filters, etc.) </li> </ul> </div> @@ -335,14 +337,18 @@ <h2 class="text-xl font-semibold">How do I erase my data from my device?</h2> <p> - Before doing this, you may want to back up your data with the instructions above. + Before doing this, you should back up your data with the instructions above. + Note that this does not erase data sent to our servers (see contact info below) </p> <ul> <li class="list-disc list-outside ml-4"> Mobile <ul> <li class="list-disc list-outside ml-4"> - Home Screen: hold down on the icon, and choose to delete it + App Store app: hold down on the icon, then uninstall it + </li> + <li class="list-disc list-outside ml-4"> + Home Screen PWA: hold down on the icon, and delete it </li> <li class="list-disc list-outside ml-4"> Chrome: Settings -> Privacy and Security -> Clear Browsing Data @@ -414,15 +420,6 @@ different page. </p> - <h2 class="text-xl font-semibold"> - Where do I get help with notifications? - </h2> - <p> - <router-link class="text-blue-500" to="/help-notifications" - >Here.</router-link - > - </p> - <h2 class="text-xl font-semibold"> This app is misbehaving, like showing me a blank screen or failing to show my personal data. What can I do? @@ -433,10 +430,13 @@ </p> <ul class="list-disc list-outside ml-4"> <li> - Drag down on the screen to refresh it; do that multiple times, because + For mobile apps, make sure you're connected to the internet. + </li> + <li> + For PWAs, drag down on the screen to refresh it; do that multiple times, because it sometimes takes multiple tries for the app to refresh to the latest version. You can see the version information at the bottom of this page; the best - way to determine the latest version is to open this page in an incognito/private + way to determine the latest version is to open TimeSafari.app in an incognito/private browser window and look at the version there. </li> <li> @@ -467,9 +467,6 @@ </ul> Then reload Time Safari. </li> - <li> - Restart your device. - </li> </ul> <p> If you still have problems, you can clear the cache (see "erase my data" above) @@ -507,16 +504,12 @@ </p> <ul class="list-disc list-outside ml-4"> <li> - If using notifications, a server stores push token data. That can be revoked at any time - by disabling notifications on the Profile <font-awesome icon="circle-user" class="fa-fw" /> page. - </li> - <li> - If sending images, a server stores them, too. They can be removed by editing the claim - and deleting them. + If sending images, a server stores them. They can be removed by editing each claim + and deleting the image. </li> <li> If sending other partner system data (eg. to Trustroots) a public key and message - data are stored on a server. Those can be removed via direct personal request. + data are stored on a server. Those can be removed via direct personal request (via contact below). </li> <li> For all other claim data, @@ -565,22 +558,22 @@ <h2 class="text-xl font-semibold">What app version is this?</h2> <p>{{ package.version }} ({{ commitHash }})</p> - <div v-if="Capacitor.isNativePlatform()"> + <div v-if="isCapacitor"> <h2 class="text-xl font-semibold"> Do I have the latest version? </h2> - <p v-if="Capacitor.getPlatform() === 'ios'"> + <p v-if="capabilities.isIOS"> <a href="https://apps.apple.com/us/app/time-safari/id6742664907" target="_blank" class="text-blue-500"> Check the App Store. </a> </p> - <p v-else-if="Capacitor.getPlatform() === 'android'"> + <p v-else-if="!capabilities.isIOS && capabilities.isMobile"> <a href="https://play.google.com/store/apps/details?id=app.timesafari.app" target="_blank" class="text-blue-500"> Check the Play Store. </a> </p> <p v-else> - Sorry, your platform of '{{ Capacitor.getPlatform() }}' is not recognized. + Sorry, your platform is not recognized. </p> </div> </div> @@ -592,12 +585,14 @@ import { Component, Vue } from "vue-facing-decorator"; import { Router } from "vue-router"; import { useClipboard } from "@vueuse/core"; -import { Capacitor } from "@capacitor/core"; +// Capacitor import removed - using QRNavigationService instead import * as Package from "../../package.json"; import QuickNav from "../components/QuickNav.vue"; import { APP_SERVER } from "../constants/app"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; +import { QRNavigationService } from "@/services/QRNavigationService"; +import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; /** * HelpView.vue - Comprehensive Help System Component @@ -643,7 +638,14 @@ export default class HelpView extends Vue { showVerifiable = false; APP_SERVER = APP_SERVER; - Capacitor = Capacitor; + // Capacitor reference removed - using QRNavigationService instead + + /** + * Get the unnamed entity name constant + */ + get unnamedEntityName(): string { + return UNNAMED_ENTITY_NAME; + } // Ideally, we put no functionality in here, especially in the setup, // because we never want this page to have a chance of throwing an error. @@ -711,11 +713,10 @@ export default class HelpView extends Vue { * @private */ private handleQRCodeClick(): void { - if (Capacitor.isNativePlatform()) { - this.$router.push({ name: "contact-qr-scan-full" }); - } else { - this.$router.push({ name: "contact-qr" }); - } + const qrNavigationService = QRNavigationService.getInstance(); + const route = qrNavigationService.getQRScannerRoute(); + + this.$router.push(route); } /** diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 4af9b5bc..45a5d5bb 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -86,33 +86,14 @@ Raymer * @version 1.0.0 */ Identity creation is now handled by router navigation guard. --> <div class="mb-4"> - <div + <RegistrationNotice v-if="!isUserRegistered" - id="noticeSomeoneMustRegisterYou" - class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4" - > - To share, someone must register you. - <div class="block text-center"> - <button - class="text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md" - @click="showNameThenIdDialog()" - > - Show them {{ PASSKEYS_ENABLED ? "default" : "your" }} identifier - info - </button> - </div> - <UserNameDialog ref="userNameDialog" /> - <div class="flex justify-end w-full"> - <router-link - :to="{ name: 'start' }" - class="block text-right text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md" - > - See advanced options - </router-link> - </div> - </div> + :passkeys-enabled="PASSKEYS_ENABLED" + :given-name="givenName" + message="To share, someone must register you." + /> - <div v-else id="sectionRecordSomethingGiven"> + <div v-if="isUserRegistered" id="sectionRecordSomethingGiven"> <!-- Record Quick-Action --> <div class="mb-6"> <div class="flex gap-2 items-center mb-2"> @@ -252,8 +233,6 @@ Raymer * @version 1.0.0 */ </div> </section> - <ChoiceButtonDialog ref="choiceButtonDialog" /> - <ImageViewer v-model:is-open="isImageViewerOpen" :image-url="selectedImage" /> </template> @@ -261,7 +240,6 @@ Raymer * @version 1.0.0 */ import { UAParser } from "ua-parser-js"; import { Component, Vue } from "vue-facing-decorator"; import { Router } from "vue-router"; -import { Capacitor } from "@capacitor/core"; //import App from "../App.vue"; import EntityIcon from "../components/EntityIcon.vue"; @@ -272,10 +250,9 @@ import InfiniteScroll from "../components/InfiniteScroll.vue"; import OnboardingDialog from "../components/OnboardingDialog.vue"; import QuickNav from "../components/QuickNav.vue"; import TopMessage from "../components/TopMessage.vue"; -import UserNameDialog from "../components/UserNameDialog.vue"; -import ChoiceButtonDialog from "../components/ChoiceButtonDialog.vue"; import ImageViewer from "../components/ImageViewer.vue"; import ActivityListItem from "../components/ActivityListItem.vue"; +import RegistrationNotice from "../components/RegistrationNotice.vue"; import { AppString, NotificationIface, @@ -305,6 +282,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { NOTIFY_CONTACT_LOADING_ISSUE } from "@/constants/notifications"; import * as Package from "../../package.json"; +import { UNNAMED_ENTITY_NAME } from "@/constants/entities"; // consolidate this with GiveActionClaim in src/interfaces/claims.ts interface Claim { @@ -383,12 +361,11 @@ interface FeedError { GiftedPrompts, InfiniteScroll, OnboardingDialog, - ChoiceButtonDialog, QuickNav, TopMessage, - UserNameDialog, ImageViewer, ActivityListItem, + RegistrationNotice, }, mixins: [PlatformServiceMixin], }) @@ -476,7 +453,7 @@ export default class HomeView extends Vue { // Re-initialize identity with new settings (loads settings internally) await this.initializeIdentity(); } else { - logger.info( + logger.debug( "[HomeView Settings Trace] 📍 DID unchanged, skipping re-initialization", ); } @@ -591,10 +568,27 @@ export default class HomeView extends Vue { this.isRegistered = true; } } catch (error) { + // Enhanced error logging with server context + const errorMessage = + error instanceof Error ? error.message : String(error); + const axiosError = error as { + response?: { + data?: { error?: { code?: string; message?: string } }; + status?: number; + }; + }; + logger.warn( "[HomeView Settings Trace] ⚠️ Registration check failed", { - error: error instanceof Error ? error.message : String(error), + error: errorMessage, + did: this.activeDid, + server: this.apiServer, + errorCode: axiosError?.response?.data?.error?.code, + errorMessage: axiosError?.response?.data?.error?.message, + httpStatus: axiosError?.response?.status, + needsUserMigration: true, + timestamp: new Date().toISOString(), }, ); } @@ -608,8 +602,7 @@ export default class HomeView extends Vue { } /** - * Ensures API server is correctly set for the current platform - * For Electron, always use production endpoint regardless of saved settings + * Ensures correct API server configuration * * @internal * Called after loading settings to ensure correct API endpoint @@ -617,12 +610,9 @@ export default class HomeView extends Vue { private async ensureCorrectApiServer() { const { DEFAULT_ENDORSER_API_SERVER } = await import("../constants/app"); - if (process.env.VITE_PLATFORM === "electron") { - // **CRITICAL FIX**: Always use production API server for Electron - // This prevents the capacitor-electron:// protocol from being used for API calls - this.apiServer = DEFAULT_ENDORSER_API_SERVER; - } else if (!this.apiServer) { - // **FIX**: Set default API server for web/development if not already set + // Only set default if no user preference exists + if (!this.apiServer) { + // Set default API server for any platform if not already set this.apiServer = DEFAULT_ENDORSER_API_SERVER; } } @@ -756,17 +746,34 @@ export default class HomeView extends Vue { * Called by FeedFilters component when filters change */ async reloadFeedOnChange() { - const settings = await this.$accountSettings(this.activeDid, { - filterFeedByVisible: false, - filterFeedByNearby: false, + logger.debug("[HomeView] 🔄 reloadFeedOnChange() called - refreshing feed"); + + // Get current settings without overriding with defaults + const settings = await this.$accountSettings(this.activeDid); + + logger.debug("[HomeView] 📊 Current filter settings:", { + filterFeedByVisible: settings.filterFeedByVisible, + filterFeedByNearby: settings.filterFeedByNearby, + searchBoxes: settings.searchBoxes?.length || 0, }); + this.isFeedFilteredByVisible = !!settings.filterFeedByVisible; this.isFeedFilteredByNearby = !!settings.filterFeedByNearby; this.isAnyFeedFilterOn = checkIsAnyFeedFilterOn(settings); + logger.debug("[HomeView] 🎯 Updated filter states:", { + isFeedFilteredByVisible: this.isFeedFilteredByVisible, + isFeedFilteredByNearby: this.isFeedFilteredByNearby, + isAnyFeedFilterOn: this.isAnyFeedFilterOn, + }); + this.feedData = []; this.feedPreviousOldestId = undefined; + + logger.debug("[HomeView] 🧹 Cleared feed data, calling updateAllFeed()"); await this.updateAllFeed(); + + logger.debug("[HomeView] ✅ Feed refresh completed"); } /** @@ -845,6 +852,14 @@ export default class HomeView extends Vue { * - this.feedLastViewedClaimId (via updateFeedLastViewedId) */ async updateAllFeed() { + logger.debug("[HomeView] 🚀 updateAllFeed() called", { + isFeedLoading: this.isFeedLoading, + currentFeedDataLength: this.feedData.length, + isAnyFeedFilterOn: this.isAnyFeedFilterOn, + isFeedFilteredByVisible: this.isFeedFilteredByVisible, + isFeedFilteredByNearby: this.isFeedFilteredByNearby, + }); + this.isFeedLoading = true; let endOfResults = true; @@ -853,21 +868,37 @@ export default class HomeView extends Vue { this.apiServer, this.feedPreviousOldestId, ); + + logger.debug("[HomeView] 📡 Retrieved gives from API", { + resultsCount: results.data.length, + endOfResults, + }); + if (results.data.length > 0) { endOfResults = false; // gather any contacts that user has blocked from view await this.processFeedResults(results.data); await this.updateFeedLastViewedId(results.data); + + logger.debug("[HomeView] 📝 Processed feed results", { + processedCount: this.feedData.length, + }); } } catch (e) { + logger.error("[HomeView] ❌ Error in updateAllFeed:", e); this.handleFeedError(e); } if (this.feedData.length === 0 && !endOfResults) { + logger.debug("[HomeView] 🔄 No results after filtering, retrying..."); await this.updateAllFeed(); } this.isFeedLoading = false; + logger.debug("[HomeView] ✅ updateAllFeed() completed", { + finalFeedDataLength: this.feedData.length, + isFeedLoading: this.isFeedLoading, + }); } /** @@ -892,12 +923,35 @@ export default class HomeView extends Vue { * @param records Array of feed records to process */ private async processFeedResults(records: GiveSummaryRecord[]) { + logger.debug("[HomeView] 📝 Processing feed results:", { + inputRecords: records.length, + currentFilters: { + isAnyFeedFilterOn: this.isAnyFeedFilterOn, + isFeedFilteredByVisible: this.isFeedFilteredByVisible, + isFeedFilteredByNearby: this.isFeedFilteredByNearby, + }, + }); + + let processedCount = 0; + let filteredCount = 0; + for (const record of records) { const processedRecord = await this.processRecord(record); if (processedRecord) { this.feedData.push(processedRecord); + processedCount++; + } else { + filteredCount++; } } + + logger.debug("[HomeView] 📊 Feed processing results:", { + processed: processedCount, + filtered: filteredCount, + total: records.length, + finalFeedLength: this.feedData.length, + }); + this.feedPreviousOldestId = records[records.length - 1].jwtId; } @@ -931,7 +985,7 @@ export default class HomeView extends Vue { * - this.feedData (via createFeedRecord) * * @param record The record to process - * @returns Processed record with contact info if it passes filters, null otherwise + * @returns Processed record if it passes filters, null otherwise */ private async processRecord( record: GiveSummaryRecord, @@ -941,13 +995,28 @@ export default class HomeView extends Vue { const recipientDid = this.extractRecipientDid(claim); const fulfillsPlan = await this.getFulfillsPlan(record); + + // Log record details for debugging + logger.debug("[HomeView] 🔍 Processing record:", { + recordId: record.jwtId, + hasFulfillsPlan: !!fulfillsPlan, + fulfillsPlanHandleId: record.fulfillsPlanHandleId, + filters: { + isAnyFeedFilterOn: this.isAnyFeedFilterOn, + isFeedFilteredByVisible: this.isFeedFilteredByNearby, + isFeedFilteredByNearby: this.isFeedFilteredByNearby, + }, + }); + if (!this.shouldIncludeRecord(record, fulfillsPlan)) { + logger.debug("[HomeView] ❌ Record filtered out:", record.jwtId); return null; } const provider = this.extractProvider(claim); const providedByPlan = await this.getProvidedByPlan(provider); + logger.debug("[HomeView] ✅ Record included:", record.jwtId); return this.createFeedRecord( record, claim, @@ -1096,6 +1165,26 @@ export default class HomeView extends Vue { } } + // Add debug logging for nearby filter + if (this.isFeedFilteredByNearby && record.fulfillsPlanHandleId) { + logger.debug("[HomeView] 🔍 Nearby filter check:", { + recordId: record.jwtId, + hasFulfillsPlan: !!fulfillsPlan, + hasLocation: !!(fulfillsPlan?.locLat && fulfillsPlan?.locLon), + location: fulfillsPlan + ? { lat: fulfillsPlan.locLat, lon: fulfillsPlan.locLon } + : null, + inSearchBox: + fulfillsPlan?.locLat && fulfillsPlan?.locLon + ? this.latLongInAnySearchBox( + fulfillsPlan.locLat, + fulfillsPlan.locLon, + ) + : null, + finalResult: anyMatch, + }); + } + return anyMatch; } @@ -1475,30 +1564,41 @@ export default class HomeView extends Vue { * @param giver Optional contact info for giver * @param description Optional gift description */ - openDialog(giver?: GiverReceiverInputInfo | "Unnamed", prompt?: string) { - if (giver === "Unnamed") { - // Special case: Pass undefined to trigger Step 1, but with "Unnamed" pre-selected - (this.$refs.giftedDialog as GiftedDialog).open( - undefined, - { - did: this.activeDid, - name: "You", - } as GiverReceiverInputInfo, - undefined, - prompt, - ); - // Immediately select "Unnamed" and move to Step 2 - (this.$refs.giftedDialog as GiftedDialog).selectGiver(); + openDialog(giver?: GiverReceiverInputInfo, prompt?: string) { + // Determine the giver entity based on DID logic + const giverEntity = this.createGiverEntity(giver); + + (this.$refs.giftedDialog as GiftedDialog).open( + giverEntity, + { + did: this.activeDid, + name: "You", // In HomeView, we always use "You" as the giver + } as GiverReceiverInputInfo, + undefined, + prompt, + ); + } + + /** + * Creates giver entity using DID-based logic + */ + private createGiverEntity( + giver?: GiverReceiverInputInfo, + ): GiverReceiverInputInfo | undefined { + if (!giver) { + return undefined; + } + + // Handle GiverReceiverInputInfo object + if (giver.did === this.activeDid) { + // If DID matches active DID, create "You" entity + return { did: this.activeDid, name: "You" }; + } else if (!giver.did || giver.did === "") { + // If DID is empty/null, create "Unnamed" entity + return { did: "", name: UNNAMED_ENTITY_NAME }; } else { - (this.$refs.giftedDialog as GiftedDialog).open( - giver, - { - did: this.activeDid, - name: "You", - } as GiverReceiverInputInfo, - undefined, - prompt, - ); + // Return the giver as-is + return giver; } } @@ -1531,7 +1631,10 @@ export default class HomeView extends Vue { * Called by template click handler */ openFeedFilters() { - (this.$refs.feedFilters as FeedFilters).open(this.reloadFeedOnChange); + (this.$refs.feedFilters as FeedFilters).open( + this.reloadFeedOnChange, + this.activeDid, + ); } /** @@ -1546,67 +1649,6 @@ export default class HomeView extends Vue { return known ? "text-slate-500" : "text-slate-100"; } - /** - * Shows name input dialog if needed - * - * @public - * @callGraph - * Called by: Template - * Calls: - * - UserNameDialog.open() - * - promptForShareMethod() - * - * @chain - * Template -> showNameThenIdDialog() -> promptForShareMethod() - * - * @requires - * - this.$refs.userNameDialog - * - this.givenName - */ - showNameThenIdDialog() { - if (!this.givenName) { - (this.$refs.userNameDialog as UserNameDialog).open(() => { - this.promptForShareMethod(); - }); - } else { - this.promptForShareMethod(); - } - } - - /** - * Shows dialog for sharing method selection - * - * @internal - * @callGraph - * Called by: showNameThenIdDialog() - * Calls: ChoiceButtonDialog.open() - * - * @chain - * Template -> showNameThenIdDialog() -> promptForShareMethod() - * - * @requires - * - this.$refs.choiceButtonDialog - * - this.$router - */ - promptForShareMethod() { - (this.$refs.choiceButtonDialog as ChoiceButtonDialog).open({ - title: "How can you share your info?", - text: "", - option1Text: "We are in a meeting together", - option2Text: "We are nearby with cameras", - option3Text: "We will share some other way", - onOption1: () => { - this.$router.push({ name: "onboard-meeting-list" }); - }, - onOption2: () => { - this.handleQRCodeClick(); - }, - onOption3: () => { - this.$router.push({ name: "share-my-contact-info" }); - }, - }); - } - /** * Opens image viewer dialog * @@ -1627,10 +1669,7 @@ export default class HomeView extends Vue { } } - openPersonDialog( - giver?: GiverReceiverInputInfo | "Unnamed", - prompt?: string, - ) { + openPersonDialog(giver?: GiverReceiverInputInfo, prompt?: string) { this.showProjectsDialog = false; this.openDialog(giver, prompt); } diff --git a/src/views/IdentitySwitcherView.vue b/src/views/IdentitySwitcherView.vue index 59eedbdf..4de52280 100644 --- a/src/views/IdentitySwitcherView.vue +++ b/src/views/IdentitySwitcherView.vue @@ -229,12 +229,14 @@ export default class IdentitySwitcherView extends Vue { if (did) { try { const newSettings = await this.$accountSettings(did); - logger.info( + logger.debug( "[IdentitySwitcher Settings Trace] ✅ New account settings loaded", { did, settingsKeys: Object.keys(newSettings).filter( - (k) => (newSettings as any)[k] !== undefined, + (k) => + k in newSettings && + newSettings[k as keyof typeof newSettings] !== undefined, ), }, ); @@ -250,7 +252,7 @@ export default class IdentitySwitcherView extends Vue { } } - logger.info( + logger.debug( "[IdentitySwitcher Settings Trace] 🔄 Navigating to home to trigger watcher", { newDid: did, diff --git a/src/views/ImportAccountView.vue b/src/views/ImportAccountView.vue index 3c87e681..d4588423 100644 --- a/src/views/ImportAccountView.vue +++ b/src/views/ImportAccountView.vue @@ -88,9 +88,15 @@ import { Router } from "vue-router"; import { AppString, NotificationIface } from "../constants/app"; import { DEFAULT_ROOT_DERIVATION_PATH } from "../libs/crypto"; -import { retrieveAccountCount, importFromMnemonic } from "../libs/util"; +import { + retrieveAccountCount, + importFromMnemonic, + checkForDuplicateAccount, + DUPLICATE_ACCOUNT_ERROR, +} from "../libs/util"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; +import { NOTIFY_DUPLICATE_ACCOUNT_IMPORT } from "@/constants/notifications"; /** * Import Account View Component @@ -198,6 +204,19 @@ export default class ImportAccountView extends Vue { } try { + // Check for duplicate account before importing + const isDuplicate = await checkForDuplicateAccount( + this.mnemonic, + this.derivationPath, + ); + if (isDuplicate) { + this.notify.warning( + NOTIFY_DUPLICATE_ACCOUNT_IMPORT.message, + TIMEOUTS.LONG, + ); + return; + } + await importFromMnemonic( this.mnemonic, this.derivationPath, @@ -221,10 +240,22 @@ export default class ImportAccountView extends Vue { this.notify.success("Account imported successfully!", TIMEOUTS.STANDARD); this.$router.push({ name: "account" }); - } catch (error: any) { + } catch (error: unknown) { this.$logError("Import failed: " + error); + + // Check if this is a duplicate account error from saveNewIdentity + const errorMessage = + error instanceof Error ? error.message : String(error); + if (errorMessage.includes(DUPLICATE_ACCOUNT_ERROR)) { + this.notify.warning( + NOTIFY_DUPLICATE_ACCOUNT_IMPORT.message, + TIMEOUTS.LONG, + ); + return; + } + this.notify.error( - error.message || "Failed to import account.", + errorMessage || "Failed to import account.", TIMEOUTS.LONG, ); } diff --git a/src/views/ImportDerivedAccountView.vue b/src/views/ImportDerivedAccountView.vue index 4c48fa20..2772cbe8 100644 --- a/src/views/ImportDerivedAccountView.vue +++ b/src/views/ImportDerivedAccountView.vue @@ -83,11 +83,12 @@ import { retrieveAllAccountsMetadata, retrieveFullyDecryptedAccount, saveNewIdentity, + checkForDuplicateAccount, } from "../libs/util"; import { logger } from "../utils/logger"; import { Account, AccountEncrypted } from "../db/tables/accounts"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; -import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; +import { createNotifyHelpers, TIMEOUTS, NotifyFunction } from "@/utils/notify"; import { NOTIFY_ACCOUNT_DERIVATION_SUCCESS, NOTIFY_ACCOUNT_DERIVATION_ERROR, @@ -100,7 +101,7 @@ import { export default class ImportAccountView extends Vue { $route!: RouteLocationNormalizedLoaded; $router!: Router; - $notify!: (notification: any, timeout?: number) => void; + $notify!: NotifyFunction; notify!: ReturnType<typeof createNotifyHelpers>; @@ -171,6 +172,16 @@ export default class ImportAccountView extends Vue { const newId = newIdentifier(address, publicHex, privateHex, newDerivPath); try { + // Check for duplicate account before creating + const isDuplicate = await checkForDuplicateAccount(newId.did); + if (isDuplicate) { + this.notify.warning( + "This derived account already exists. Please try a different derivation path.", + TIMEOUTS.LONG, + ); + return; + } + await saveNewIdentity(newId, mne, newDerivPath); // record that as the active DID diff --git a/src/views/NewEditAccountView.vue b/src/views/NewEditAccountView.vue index 78e709f2..d1349e5a 100644 --- a/src/views/NewEditAccountView.vue +++ b/src/views/NewEditAccountView.vue @@ -110,10 +110,22 @@ export default class NewEditAccountView extends Vue { * @async */ async onClickSaveChanges() { - await this.$updateSettings({ - firstName: this.givenName, - lastName: "", // deprecated, pre v 0.1.3 - }); + // Get the current active DID to save to user-specific settings + const settings = await this.$accountSettings(); + const activeDid = settings.activeDid; + + if (activeDid) { + // Save to user-specific settings for the current identity + await this.$saveUserSettings(activeDid, { + firstName: this.givenName, + }); + } else { + // Fallback to master settings if no active DID + await this.$saveSettings({ + firstName: this.givenName, + }); + } + this.$router.back(); } diff --git a/src/views/OnboardMeetingListView.vue b/src/views/OnboardMeetingListView.vue index 975e914b..91dccb3b 100644 --- a/src/views/OnboardMeetingListView.vue +++ b/src/views/OnboardMeetingListView.vue @@ -251,7 +251,7 @@ export default class OnboardMeetingListView extends Vue { if (response2.data?.data) { this.meetings = response2.data.data; } - } catch (error: any) { + } catch (error: unknown) { this.$logAndConsole( "Error fetching meetings: " + errorStringForLog(error), true, diff --git a/src/views/OnboardMeetingMembersView.vue b/src/views/OnboardMeetingMembersView.vue index a1280011..7e42718b 100644 --- a/src/views/OnboardMeetingMembersView.vue +++ b/src/views/OnboardMeetingMembersView.vue @@ -113,7 +113,7 @@ export default class OnboardMeetingMembersView extends Vue { try { // Identity creation should be handled by router guard, but keep as fallback for meeting setup if (!this.activeDid) { - logger.info( + this.$logAndConsole( "[OnboardMeetingMembersView] No active DID found, creating identity as fallback for meeting setup", ); this.activeDid = await generateSaveAndActivateIdentity(); diff --git a/src/views/OnboardMeetingSetupView.vue b/src/views/OnboardMeetingSetupView.vue index a313f658..95e87b89 100644 --- a/src/views/OnboardMeetingSetupView.vue +++ b/src/views/OnboardMeetingSetupView.vue @@ -345,7 +345,9 @@ export default class OnboardMeetingView extends Vue { } async created() { - this.notify = createNotifyHelpers(this.$notify as any); + this.notify = createNotifyHelpers( + this.$notify as Parameters<typeof createNotifyHelpers>[0], + ); const settings = await this.$accountSettings(); this.activeDid = settings?.activeDid || ""; this.apiServer = settings?.apiServer || ""; @@ -419,7 +421,7 @@ export default class OnboardMeetingView extends Vue { } else { this.newOrUpdatedMeetingInputs = this.blankMeeting(); } - } catch (error: any) { + } catch (error: unknown) { this.newOrUpdatedMeetingInputs = this.blankMeeting(); } } diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue index f0a5a4c1..361c822f 100644 --- a/src/views/ProjectViewView.vue +++ b/src/views/ProjectViewView.vue @@ -183,7 +183,7 @@ class="text-blue-500" @click="onClickLoadProject(plan.handleId)" > - {{ plan.name || "Unnamed Project" }} + {{ plan.name || unnamedProject }} </button> </div> <div v-if="fulfillersToHitLimit" class="text-center"> @@ -207,7 +207,7 @@ class="text-blue-500" @click="onClickLoadProject(fulfilledByThis.handleId)" > - {{ fulfilledByThis.name || "Unnamed Project" }} + {{ fulfilledByThis.name || unnamedProject }} </button> </div> </div> @@ -226,7 +226,7 @@ <div class="grid items-start grid-cols-1 sm:grid-cols-3 gap-4 mt-4"> <!-- First, offers on the left--> <div class="bg-slate-100 px-4 py-3 rounded-md"> - <div v-if="activeDid && isRegistered"> + <div v-if="activeDid && isRegistered" class="mb-4"> <div class="text-center"> <button data-testId="offerButton" @@ -243,13 +243,19 @@ :project-name="name" /> - <h3 class="text-lg font-bold mb-3 mt-4">Offered To This Idea</h3> + <h3 class="text-lg font-bold leading-tight mb-3"> + Offered To This Idea + </h3> - <div v-if="offersToThis.length === 0"> - (None yet. Wanna - <span class="cursor-pointer text-blue-500" @click="openOfferDialog()" - >offer something... especially if others join you</span - >?) + <div v-if="offersToThis.length === 0" class="text-sm"> + (None yet.<span v-if="activeDid && isRegistered"> + Wanna + <span + class="cursor-pointer text-blue-500" + @click="openOfferDialog()" + >offer something… especially if others join you</span + >?</span + >) </div> <ul v-else class="text-sm border-t border-slate-300"> @@ -314,7 +320,7 @@ <!-- Now, gives TO this project in the middle --> <!-- (similar to "FROM" gift display below) --> <div class="bg-slate-100 px-4 py-3 rounded-md" data-testId="gives-to"> - <div v-if="activeDid && isRegistered"> + <div v-if="activeDid && isRegistered" class="mb-4"> <div class="text-center"> <button class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 text-sm leading-tight rounded-md" @@ -325,7 +331,9 @@ </div> </div> - <h3 class="text-lg font-bold mt-4">Given To This Project</h3> + <h3 class="text-lg font-bold leading-tight mb-3"> + Given To This Project + </h3> <div v-if="givesToThis.length === 0" class="text-sm"> (None yet. If you've seen something, say something by clicking a @@ -476,7 +484,7 @@ <!-- Finally, gives FROM this project on the right --> <!-- (similar to "TO" gift display above) --> <div class="bg-slate-100 px-4 py-3 rounded-md" data-testId="gives-from"> - <div v-if="activeDid && isRegistered"> + <div v-if="activeDid && isRegistered" class="mb-4"> <div class="text-center"> <button class="block w-full bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 text-sm leading-tight rounded-md" @@ -494,11 +502,13 @@ :is-from-project-view="true" /> - <h3 class="text-lg font-bold mb-3 mt-4"> + <h3 class="text-lg font-bold leading-tight mb-3"> Benefitted From This Project </h3> - <div v-if="givesProvidedByThis.length === 0">(None yet.)</div> + <div v-if="givesProvidedByThis.length === 0" class="text-sm"> + (None yet.) + </div> <ul v-else class="text-sm border-t border-slate-300"> <li @@ -611,6 +621,7 @@ import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; import { NOTIFY_CONFIRM_CLAIM } from "@/constants/notifications"; import { APP_SERVER } from "@/constants/app"; +import { UNNAMED_PROJECT } from "@/constants/entities"; /** * Project View Component * @author Matthew Raymer @@ -664,6 +675,13 @@ export default class ProjectViewView extends Vue { /** Notification helpers instance */ notify!: ReturnType<typeof createNotifyHelpers>; + /** + * Get the unnamed project constant + */ + get unnamedProject(): string { + return UNNAMED_PROJECT; + } + // Account and Settings State /** Currently active DID */ activeDid = ""; diff --git a/src/views/ProjectsView.vue b/src/views/ProjectsView.vue index 7c4f0560..9af4aaef 100644 --- a/src/views/ProjectsView.vue +++ b/src/views/ProjectsView.vue @@ -216,15 +216,12 @@ <font-awesome icon="plus" :class="plusIconClasses" /> button. You'll never know until you try. </div> - <div v-else> - <button - :class="onboardingButtonClasses" - @click="showNameThenIdDialog()" - > - Get someone to onboard you. - </button> - <UserNameDialog ref="userNameDialog" /> - </div> + <RegistrationNotice + v-else + :passkeys-enabled="PASSKEYS_ENABLED" + :given-name="givenName" + message="To announce a project, get someone to onboard you." + /> </div> <ul id="listProjects" class="border-t border-slate-300"> <li @@ -247,7 +244,7 @@ <div class="grow overflow-hidden"> <h2 class="text-base font-semibold"> - {{ project.name || "Unnamed Project" }} + {{ project.name || unnamedProject }} </h2> <div class="text-sm truncate"> {{ project.description }} @@ -264,16 +261,16 @@ import { AxiosRequestConfig } from "axios"; import { Component, Vue } from "vue-facing-decorator"; import { Router } from "vue-router"; -import { Capacitor } from "@capacitor/core"; +// Capacitor import removed - using QRNavigationService instead -import { NotificationIface } from "../constants/app"; +import { NotificationIface, PASSKEYS_ENABLED } from "../constants/app"; import EntityIcon from "../components/EntityIcon.vue"; import InfiniteScroll from "../components/InfiniteScroll.vue"; import QuickNav from "../components/QuickNav.vue"; import OnboardingDialog from "../components/OnboardingDialog.vue"; import ProjectIcon from "../components/ProjectIcon.vue"; import TopMessage from "../components/TopMessage.vue"; -import UserNameDialog from "../components/UserNameDialog.vue"; +import RegistrationNotice from "../components/RegistrationNotice.vue"; import { Contact } from "../db/tables/contacts"; import { didInfo, getHeaders, getPlanFromCache } from "../libs/endorserServer"; import { OfferSummaryRecord, PlanData } from "../interfaces/records"; @@ -281,14 +278,15 @@ import { OnboardPage, iconForUnitCode } from "../libs/util"; import { logger } from "../utils/logger"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify"; + import { NOTIFY_NO_ACCOUNT_ERROR, NOTIFY_PROJECT_LOAD_ERROR, NOTIFY_PROJECT_INIT_ERROR, NOTIFY_OFFERS_LOAD_ERROR, NOTIFY_OFFERS_FETCH_ERROR, - NOTIFY_CAMERA_SHARE_METHOD, } from "@/constants/notifications"; +import { UNNAMED_PROJECT } from "@/constants/entities"; /** * Projects View Component @@ -317,7 +315,7 @@ import { OnboardingDialog, ProjectIcon, TopMessage, - UserNameDialog, + RegistrationNotice, }, mixins: [PlatformServiceMixin], }) @@ -327,6 +325,13 @@ export default class ProjectsView extends Vue { notify!: ReturnType<typeof createNotifyHelpers>; + /** + * Get the unnamed project constant + */ + get unnamedProject(): string { + return UNNAMED_PROJECT; + } + // User account state activeDid = ""; allContacts: Array<Contact> = []; @@ -335,6 +340,7 @@ export default class ProjectsView extends Vue { givenName = ""; isLoading = false; isRegistered = false; + readonly PASSKEYS_ENABLED: boolean = PASSKEYS_ENABLED; // Data collections offers: OfferSummaryRecord[] = []; @@ -464,8 +470,10 @@ export default class ProjectsView extends Vue { ); this.notify.error(NOTIFY_PROJECT_LOAD_ERROR.message, TIMEOUTS.LONG); } - } catch (error: any) { - logger.error("Got error loading plans:", error.message || error); + } catch (error: unknown) { + const errorMessage = + error instanceof Error ? error.message : String(error); + logger.error("Got error loading plans:", errorMessage); this.notify.error(NOTIFY_PROJECT_LOAD_ERROR.message, TIMEOUTS.LONG); } finally { this.isLoading = false; @@ -578,8 +586,10 @@ export default class ProjectsView extends Vue { ); this.notify.error(NOTIFY_OFFERS_LOAD_ERROR.message, TIMEOUTS.LONG); } - } catch (error: any) { - logger.error("Got error loading offers:", error.message || error); + } catch (error: unknown) { + const errorMessage = + error instanceof Error ? error.message : String(error); + logger.error("Got error loading offers:", errorMessage); this.notify.error(NOTIFY_OFFERS_FETCH_ERROR.message, TIMEOUTS.LONG); } finally { this.isLoading = false; @@ -619,39 +629,6 @@ export default class ProjectsView extends Vue { * Ensures user has provided their name before proceeding with contact sharing. * Uses UserNameDialog component if name is not set. */ - showNameThenIdDialog() { - if (!this.givenName) { - (this.$refs.userNameDialog as UserNameDialog).open(() => { - this.promptForShareMethod(); - }); - } else { - this.promptForShareMethod(); - } - } - - /** - * Prompts user to choose contact sharing method - * - * Presents modal dialog asking if users are nearby with cameras. - * Routes to appropriate sharing method based on user's choice: - * - QR code sharing for nearby users with cameras - * - Alternative sharing methods for remote users - */ - promptForShareMethod() { - this.$notify( - { - group: "modal", - type: "confirm", - title: NOTIFY_CAMERA_SHARE_METHOD.title, - text: NOTIFY_CAMERA_SHARE_METHOD.text, - onYes: () => this.handleQRCodeClick(), - onNo: () => this.$router.push({ name: "share-my-contact-info" }), - yesText: NOTIFY_CAMERA_SHARE_METHOD.yesText, - noText: NOTIFY_CAMERA_SHARE_METHOD.noText, - }, - TIMEOUTS.MODAL, - ); - } /** * Computed properties for template logic streamlining @@ -717,14 +694,6 @@ export default class ProjectsView extends Vue { return "bg-green-600 text-white px-1.5 py-1 rounded-full"; } - /** - * CSS class names for onboarding button - * @returns String with CSS classes for the onboarding button - */ - get onboardingButtonClasses() { - return "text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"; - } - /** * CSS class names for project tab styling * @returns Object with CSS classes based on current tab selection @@ -749,21 +718,6 @@ export default class ProjectsView extends Vue { * Utility methods */ - /** - * Handles QR code sharing functionality with platform detection - * - * Routes to appropriate QR code interface based on current platform: - * - Full QR scanner for native mobile platforms - * - Web-based QR interface for browser environments - */ - private handleQRCodeClick() { - if (Capacitor.isNativePlatform()) { - this.$router.push({ name: "contact-qr-scan-full" }); - } else { - this.$router.push({ name: "contact-qr" }); - } - } - /** * Legacy method compatibility * @deprecated Use computedOfferTabClassNames for backward compatibility diff --git a/src/views/QuickActionBvcEndView.vue b/src/views/QuickActionBvcEndView.vue index f02af128..75a2bc2d 100644 --- a/src/views/QuickActionBvcEndView.vue +++ b/src/views/QuickActionBvcEndView.vue @@ -69,10 +69,17 @@ <div v-if="claimCountWithHidden > 0" class="border-b border-slate-300 pb-2"> <span> {{ claimCountWithHiddenText }} - so if you expected but do not see details from someone then ask them to - check that their activity is visible to you on their Contacts - <font-awesome icon="users" class="text-slate-500" /> - page. + If you don't see expected info above for someone, ask them to check that + their activity is visible to you ( + <font-awesome icon="arrow-up" class="fa-fw" /> + <font-awesome icon="eye" class="fa-fw" /> + ) on + <a + class="text-blue-500 underline cursor-pointer" + @click="copyContactsLinkToClipboard" + > + this page </a + >. </span> </div> <div v-if="claimCountByUser > 0" class="border-b border-slate-300 pb-2"> @@ -120,10 +127,11 @@ import { DateTime } from "luxon"; import * as R from "ramda"; import { Component, Vue } from "vue-facing-decorator"; import { Router } from "vue-router"; +import { useClipboard } from "@vueuse/core"; import QuickNav from "../components/QuickNav.vue"; import TopMessage from "../components/TopMessage.vue"; -import { NotificationIface } from "../constants/app"; +import { NotificationIface, APP_SERVER } from "../constants/app"; import { Contact } from "../db/tables/contacts"; import { GenericCredWrapper, @@ -148,6 +156,7 @@ import { NOTIFY_ALL_CONFIRMATIONS_ERROR, NOTIFY_GIVE_SEND_ERROR, NOTIFY_CLAIMS_SEND_ERROR, + NOTIFY_COPIED_TO_CLIPBOARD, createConfirmationSuccessMessage, createCombinedSuccessMessage, } from "@/constants/notifications"; @@ -195,8 +204,8 @@ export default class QuickActionBvcEndView extends Vue { get claimCountWithHiddenText() { if (this.claimCountWithHidden === 0) return ""; return this.claimCountWithHidden === 1 - ? "There is 1 other claim with hidden details," - : `There are ${this.claimCountWithHidden} other claims with hidden details,`; + ? "There is 1 other claim with hidden details." + : `There are ${this.claimCountWithHidden} other claims with hidden details.`; } get claimCountByUserText() { @@ -239,7 +248,8 @@ export default class QuickActionBvcEndView extends Vue { } const eventStartDateObj = currentOrPreviousSat .set({ weekday: 6 }) - .set({ hour: 9 }) + .set({ hour: 8 }) + .set({ minute: 30 }) // to catch if people put their claims 30 minutes early .startOf("hour"); // Hack, but full ISO pushes the length to 340 which crashes verifyJWT! @@ -295,6 +305,25 @@ export default class QuickActionBvcEndView extends Vue { (this.$router as Router).push(route); } + copyContactsLinkToClipboard() { + const deepLinkUrl = `${APP_SERVER}/deep-link/did/${this.activeDid}`; + useClipboard() + .copy(deepLinkUrl) + .then(() => { + this.notify.success( + NOTIFY_COPIED_TO_CLIPBOARD.message("Your info link"), + TIMEOUTS.SHORT, + ); + }) + .catch((error) => { + logger.error("Failed to copy to clipboard:", error); + this.notify.error( + "Failed to copy link to clipboard. Please try again.", + TIMEOUTS.SHORT, + ); + }); + } + async record() { try { if (this.claimsToConfirmSelected.length > 0) { diff --git a/src/views/ShareMyContactInfoView.vue b/src/views/ShareMyContactInfoView.vue index 2316b1d2..0476352e 100644 --- a/src/views/ShareMyContactInfoView.vue +++ b/src/views/ShareMyContactInfoView.vue @@ -144,8 +144,8 @@ export default class ShareMyContactInfoView extends Vue { * Copy the contact message to clipboard */ private async copyToClipboard(message: string): Promise<void> { - const { useClipboard } = await import("@vueuse/core"); - await useClipboard().copy(message); + const { copyToClipboard } = await import("../services/ClipboardService"); + await copyToClipboard(message); } /** diff --git a/src/views/StartView.vue b/src/views/StartView.vue index 93c196fd..de21d2e9 100644 --- a/src/views/StartView.vue +++ b/src/views/StartView.vue @@ -203,7 +203,7 @@ export default class StartView extends Vue { // Load account count for display logic this.numAccounts = await retrieveAccountCount(); - logger.info("[StartView] Component mounted", { + logger.debug("[StartView] Component mounted", { hasGivenName: !!this.givenName, accountCount: this.numAccounts, passkeysEnabled: this.PASSKEYS_ENABLED, @@ -221,7 +221,7 @@ export default class StartView extends Vue { * Routes user to new identifier creation flow with seed-based approach */ public onClickNewSeed() { - logger.info("[StartView] User selected new seed generation"); + logger.debug("[StartView] User selected new seed generation"); this.$router.push({ name: "new-identifier" }); } @@ -235,14 +235,14 @@ export default class StartView extends Vue { const keyName = AppString.APP_NAME + (this.givenName ? " - " + this.givenName : ""); - logger.info("[StartView] Initiating passkey registration", { + logger.debug("[StartView] Initiating passkey registration", { keyName, hasGivenName: !!this.givenName, }); await registerSaveAndActivatePasskey(keyName); - logger.info("[StartView] Passkey registration successful"); + logger.debug("[StartView] Passkey registration successful"); this.$router.push({ name: "account" }); } catch (error) { logger.error("[StartView] Passkey registration failed", error); @@ -255,7 +255,7 @@ export default class StartView extends Vue { * Routes user to account import flow for existing seed phrase */ public onClickNo() { - logger.info("[StartView] User selected existing seed import"); + logger.debug("[StartView] User selected existing seed import"); this.$router.push({ name: "import-account" }); } @@ -264,7 +264,7 @@ export default class StartView extends Vue { * Routes user to address derivation flow for existing seed */ public onClickDerive() { - logger.info("[StartView] User selected address derivation"); + logger.debug("[StartView] User selected address derivation"); this.$router.push({ name: "import-derive" }); } } diff --git a/src/views/TestView.vue b/src/views/TestView.vue index df879ad2..f6f1888d 100644 --- a/src/views/TestView.vue +++ b/src/views/TestView.vue @@ -91,13 +91,95 @@ name: 'shared-photo', query: { fileName }, }" - class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2" + class="block w-full text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md mb-2 mt-2" data-testId="fileUploadButton" > Go to Shared Page </router-link> </div> + <!-- URL Flow Testing Section --> + <div class="mt-8"> + <h2 class="text-xl font-bold mb-4">URL Flow Testing</h2> + <p class="text-sm text-gray-600 mb-3"> + Test claim and partner server URL flow from initialization to change + propagation. + </p> + + <div class="space-y-4"> + <div class="p-4 border border-gray-300 rounded-md bg-gray-50"> + <h3 class="font-semibold mb-2">Current URL State</h3> + <div class="space-y-2 text-sm"> + <div> + <strong>API Server:</strong> + <span class="font-mono">{{ apiServer || "Not Set" }}</span> + </div> + <div> + <strong>Partner API Server:</strong> + <span class="font-mono">{{ partnerApiServer || "Not Set" }}</span> + </div> + <div> + <strong>Active DID:</strong> + <span class="font-mono">{{ activeDid || "Not Set" }}</span> + </div> + <div> + <strong>Platform:</strong> + <span class="font-mono">{{ getCurrentPlatform() }}</span> + </div> + </div> + </div> + + <div class="space-y-2"> + <button + :class="primaryButtonClasses" + :disabled="isUrlTestRunning" + @click="testUrlFlow()" + > + {{ isUrlTestRunning ? "Testing..." : "Test URL Flow" }} + </button> + + <button :class="secondaryButtonClasses" @click="changeApiServer()"> + Change API Server (Test → Prod) + </button> + + <button + :class="secondaryButtonClasses" + @click="changePartnerApiServer()" + > + Change Partner API Server (Test → Prod) + </button> + + <button :class="warningButtonClasses" @click="resetToDefaults()"> + Reset to Defaults + </button> + + <button :class="secondaryButtonClasses" @click="refreshSettings()"> + Refresh Settings + </button> + + <button + :class="secondaryButtonClasses" + @click="logEnvironmentState()" + > + Log Environment State + </button> + </div> + + <div class="p-4 border border-gray-300 rounded-md bg-gray-50"> + <h3 class="font-semibold mb-2">URL Flow Test Results</h3> + <div class="max-h-64 overflow-y-auto space-y-2"> + <div + v-for="(result, index) in urlTestResults" + :key="index" + class="p-2 border border-gray-200 rounded text-xs font-mono bg-white" + > + {{ result }} + </div> + </div> + </div> + </div> + </div> + <div class="mt-8"> <h2 class="text-xl font-bold mb-4">Passkeys</h2> See console for results. @@ -326,6 +408,11 @@ export default class Help extends Vue { showEntityGridTest = false; showPlatformServiceTest = false; + // for URL flow testing + isUrlTestRunning = false; + urlTestResults: string[] = []; + partnerApiServer: string | undefined; + /** * Computed properties for template streamlining * Eliminates repeated classes and logic in template @@ -534,24 +621,93 @@ export default class Help extends Vue { } /** - * Component initialization - * * Loads user settings and account information for testing interface * Uses PlatformServiceMixin for database access */ async mounted() { - const settings = await this.$accountSettings(); - this.activeDid = settings.activeDid || ""; - this.apiServer = settings.apiServer || ""; - this.userName = settings.firstName; - - const account = await retrieveAccountMetadata(this.activeDid); - if (this.activeDid) { - if (account) { - this.credIdHex = account.passkeyCredIdHex as string; - } else { - alert("No account found for DID " + this.activeDid); + logger.info( + "[TestView] 🚀 Component mounting - starting URL flow tracking", + ); + + // Boot-time logging for initial configuration + logger.info("[TestView] 🌍 Boot-time configuration detected:", { + platform: process.env.VITE_PLATFORM, + defaultEndorserApiServer: process.env.VITE_DEFAULT_ENDORSER_API_SERVER, + defaultPartnerApiServer: process.env.VITE_DEFAULT_PARTNER_API_SERVER, + nodeEnv: process.env.NODE_ENV, + timestamp: new Date().toISOString(), + }); + + try { + // Track settings loading + logger.info("[TestView] 📥 Loading account settings..."); + const settings = await this.$accountSettings(); + + logger.info("[TestView] 📊 Settings loaded:", { + activeDid: settings.activeDid, + apiServer: settings.apiServer, + partnerApiServer: settings.partnerApiServer, + isRegistered: settings.isRegistered, + firstName: settings.firstName, + }); + + // Update component state + this.activeDid = settings.activeDid || ""; + this.apiServer = settings.apiServer || ""; + this.partnerApiServer = settings.partnerApiServer || ""; + this.userName = settings.firstName; + + logger.info("[TestView] ✅ Component state updated:", { + activeDid: this.activeDid, + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + }); + + // Load account metadata + if (this.activeDid) { + logger.info( + "[TestView] 🔍 Loading account metadata for DID:", + this.activeDid, + ); + const account = await retrieveAccountMetadata(this.activeDid); + + if (account) { + this.credIdHex = account.passkeyCredIdHex as string; + logger.info("[TestView] ✅ Account metadata loaded:", { + did: account.did, + hasPasskey: !!account.passkeyCredIdHex, + passkeyId: account.passkeyCredIdHex, + }); + } else { + logger.warn( + "[TestView] ⚠️ No account found for DID:", + this.activeDid, + ); + alert("No account found for DID " + this.activeDid); + } } + + logger.info("[TestView] 🎯 Component initialization complete:", { + activeDid: this.activeDid, + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + hasPasskey: !!this.credIdHex, + platform: this.getCurrentPlatform(), + }); + } catch (error) { + logger.error( + "[TestView] ❌ Error during component initialization:", + error, + ); + this.$notify( + { + group: "error", + type: "error", + title: "Initialization Error", + text: `Failed to initialize component: ${error instanceof Error ? error.message : String(error)}`, + }, + 5000, + ); } } @@ -824,5 +980,276 @@ export default class Help extends Vue { ); } } + + /** + * Tests the URL flow from initialization to change propagation. + * This simulates the flow where a user's DID is set, and then the + * claim and partner server URLs are updated. + */ + public async testUrlFlow() { + this.isUrlTestRunning = true; + this.urlTestResults = []; + + try { + logger.info("[TestView] 🔬 Starting comprehensive URL flow test"); + this.addUrlTestResult("🚀 Starting URL flow test..."); + + // Test 1: Current state + this.addUrlTestResult(`📊 Current State:`); + this.addUrlTestResult(` - API Server: ${this.apiServer || "Not Set"}`); + this.addUrlTestResult( + ` - Partner API Server: ${this.partnerApiServer || "Not Set"}`, + ); + this.addUrlTestResult(` - Active DID: ${this.activeDid || "Not Set"}`); + this.addUrlTestResult(` - Platform: ${this.getCurrentPlatform()}`); + + // Test 2: Load fresh settings + this.addUrlTestResult(`\n📥 Testing Settings Loading:`); + const startTime = Date.now(); + const settings = await this.$accountSettings(); + const loadTime = Date.now() - startTime; + + this.addUrlTestResult(` - Settings loaded in ${loadTime}ms`); + this.addUrlTestResult( + ` - API Server from settings: ${settings.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Partner API Server from settings: ${settings.partnerApiServer || "Not Set"}`, + ); + + // Test 3: Database query + this.addUrlTestResult(`\n💾 Testing Database Query:`); + const dbStartTime = Date.now(); + const dbResult = await this.$dbQuery( + "SELECT apiServer, partnerApiServer, activeDid FROM settings WHERE id = ? OR accountDid = ?", + [1, this.activeDid || ""], + ); + const dbTime = Date.now() - dbStartTime; + + if (dbResult?.values) { + this.addUrlTestResult(` - Database query completed in ${dbTime}ms`); + this.addUrlTestResult( + ` - Raw DB values: ${JSON.stringify(dbResult.values)}`, + ); + } else { + this.addUrlTestResult( + ` - Database query failed or returned no results`, + ); + } + + // Test 4: Environment variables + this.addUrlTestResult(`\n🌍 Testing Environment Variables:`); + this.addUrlTestResult( + ` - VITE_PLATFORM: ${import.meta.env.VITE_PLATFORM || "Not Set"}`, + ); + this.addUrlTestResult( + ` - VITE_DEFAULT_ENDORSER_API_SERVER: ${import.meta.env.VITE_DEFAULT_ENDORSER_API_SERVER || "Not Set"}`, + ); + this.addUrlTestResult( + ` - VITE_DEFAULT_PARTNER_API_SERVER: ${import.meta.env.VITE_DEFAULT_PARTNER_API_SERVER || "Not Set"}`, + ); + + // Test 5: Constants + this.addUrlTestResult(`\n📋 Testing App Constants:`); + this.addUrlTestResult( + ` - PROD_ENDORSER_API_SERVER: ${AppString.PROD_ENDORSER_API_SERVER}`, + ); + this.addUrlTestResult( + ` - PROD_PARTNER_API_SERVER: ${AppString.PROD_PARTNER_API_SERVER}`, + ); + + // Test 6: Change detection + this.addUrlTestResult(`\n🔄 Testing Change Detection:`); + const originalApiServer = this.apiServer; + const originalPartnerServer = this.partnerApiServer; + + // Simulate a change + this.addUrlTestResult(` - Original API Server: ${originalApiServer}`); + this.addUrlTestResult( + ` - Original Partner Server: ${originalPartnerServer}`, + ); + + // Test 7: Settings update + this.addUrlTestResult(`\n💾 Testing Settings Update:`); + const testChanges = { + apiServer: + originalApiServer === "https://api.endorser.ch" + ? "https://test-api.endorser.ch" + : "https://api.endorser.ch", + }; + + this.addUrlTestResult( + ` - Attempting to change API Server to: ${testChanges.apiServer}`, + ); + const updateResult = await this.$saveSettings(testChanges); + this.addUrlTestResult( + ` - Update result: ${updateResult ? "Success" : "Failed"}`, + ); + + // Test 8: Verify change propagation + this.addUrlTestResult(`\n✅ Testing Change Propagation:`); + const newSettings = await this.$accountSettings(); + this.addUrlTestResult( + ` - New API Server from settings: ${newSettings.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Component state API Server: ${this.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Change propagated: ${newSettings.apiServer === this.apiServer ? "Yes" : "No"}`, + ); + + // Test 9: Revert changes + this.addUrlTestResult(`\n🔄 Reverting Changes:`); + const revertResult = await this.$saveSettings({ + apiServer: originalApiServer, + }); + this.addUrlTestResult( + ` - Revert result: ${revertResult ? "Success" : "Failed"}`, + ); + + // Test 10: Final verification + this.addUrlTestResult(`\n🎯 Final Verification:`); + const finalSettings = await this.$accountSettings(); + this.addUrlTestResult( + ` - Final API Server: ${finalSettings.apiServer || "Not Set"}`, + ); + this.addUrlTestResult( + ` - Matches original: ${finalSettings.apiServer === originalApiServer ? "Yes" : "No"}`, + ); + + this.addUrlTestResult(`\n✅ URL flow test completed successfully!`); + logger.info("[TestView] ✅ URL flow test completed successfully"); + } catch (error) { + const errorMsg = `❌ URL flow test failed: ${error instanceof Error ? error.message : String(error)}`; + this.addUrlTestResult(errorMsg); + logger.error("[TestView] ❌ URL flow test failed:", error); + } finally { + this.isUrlTestRunning = false; + } + } + + /** + * Adds a result to the URL test results array. + */ + private addUrlTestResult(message: string) { + this.urlTestResults.push(message); + } + + /** + * Changes the API server to the production URL. + */ + public changeApiServer() { + const currentServer = this.apiServer; + const newServer = + currentServer === "https://api.endorser.ch" + ? "https://test-api.endorser.ch" + : "https://api.endorser.ch"; + + logger.info("[TestView] 🔄 Changing API server:", { + from: currentServer, + to: newServer, + }); + + this.apiServer = newServer; + this.addUrlTestResult( + `API Server changed from ${currentServer} to ${newServer}`, + ); + } + + /** + * Changes the partner API server to the production URL. + */ + public changePartnerApiServer() { + const currentServer = this.partnerApiServer; + const newServer = + currentServer === "https://partner-api.endorser.ch" + ? "https://test-partner-api.endorser.ch" + : "https://partner-api.endorser.ch"; + + logger.info("[TestView] 🔄 Changing partner API server:", { + from: currentServer, + to: newServer, + }); + + this.partnerApiServer = newServer; + this.addUrlTestResult( + `Partner API Server changed from ${currentServer} to ${newServer}`, + ); + } + + /** + * Resets all URL-related settings to their initial values. + */ + public resetToDefaults() { + this.apiServer = AppString.TEST_ENDORSER_API_SERVER; + this.partnerApiServer = AppString.TEST_PARTNER_API_SERVER; + this.activeDid = ""; + this.addUrlTestResult("URL Flow Test Results Reset to Defaults."); + } + + /** + * Refreshes settings from the database to verify changes. + */ + public async refreshSettings() { + try { + logger.info("[TestView] 🔄 Refreshing settings from database"); + const settings = await this.$accountSettings(); + + // Update component state + this.apiServer = settings.apiServer || ""; + this.partnerApiServer = settings.partnerApiServer || ""; + + logger.info("[TestView] ✅ Settings refreshed:", { + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + }); + + this.addUrlTestResult( + `Settings refreshed - API Server: ${this.apiServer}, Partner API Server: ${this.partnerApiServer}`, + ); + } catch (error) { + logger.error("[TestView] ❌ Error refreshing settings:", error); + this.addUrlTestResult( + `Error refreshing settings: ${error instanceof Error ? error.message : String(error)}`, + ); + } + } + + /** + * Logs the current environment state to the console. + */ + public logEnvironmentState() { + logger.info("[TestView] 🌐 Current Environment State:", { + VITE_PLATFORM: import.meta.env.VITE_PLATFORM, + VITE_DEFAULT_ENDORSER_API_SERVER: import.meta.env + .VITE_DEFAULT_ENDORSER_API_SERVER, + VITE_DEFAULT_PARTNER_API_SERVER: import.meta.env + .VITE_DEFAULT_PARTNER_API_SERVER, + NODE_ENV: process.env.NODE_ENV, + activeDid: this.activeDid, + apiServer: this.apiServer, + partnerApiServer: this.partnerApiServer, + }); + this.$notify({ + group: "info", + type: "info", + title: "Environment State Logged", + text: "Current environment state logged to console.", + }); + } + + /** + * Gets the current platform based on the API server. + */ + public getCurrentPlatform(): string { + if (this.apiServer?.includes(AppString.PROD_ENDORSER_API_SERVER)) { + return "Production"; + } else if (this.apiServer?.includes(AppString.TEST_ENDORSER_API_SERVER)) { + return "Test"; + } else { + return "Unknown"; + } + } } </script> diff --git a/test-playwright/00-noid-tests.spec.ts b/test-playwright/00-noid-tests.spec.ts index 5218c330..8da08e33 100644 --- a/test-playwright/00-noid-tests.spec.ts +++ b/test-playwright/00-noid-tests.spec.ts @@ -37,7 +37,7 @@ * Key Selectors: * - Activity list: 'ul#listLatestActivity li' * - Discover list: 'ul#listDiscoverResults li' - * - Account notices: '#noticeBeforeShare', '#noticeBeforeAnnounce' + * - Account notices: '#noticeBeforeShare', '#noticeSomeoneMustRegisterYou' * - Identity details: '#sectionIdentityDetails code.truncate' * * State Verification: @@ -69,7 +69,8 @@ */ import { test, expect } from '@playwright/test'; -import { deleteContact, generateAndRegisterEthrUser, importUser } from './testUtils'; +import { createContactName, generateNewEthrUser, importUser, importUserFromAccount } from './testUtils'; +import { NOTIFY_CONTACT_INVALID_DID } from '../src/constants/notifications'; test('Check activity feed - check that server is running', async ({ page }) => { // Load app homepage @@ -99,7 +100,7 @@ test('Check no-ID messaging in account', async ({ page }) => { await page.goto('./account'); // Check 'a friend needs to register you' notice - await expect(page.locator('#noticeBeforeAnnounce')).toBeVisible(); + await expect(page.locator('#noticeSomeoneMustRegisterYou')).toBeVisible(); }); test('Check ability to share contact', async ({ page }) => { @@ -169,36 +170,34 @@ test('Confirm test API setting (may fail if you are running your own Time Safari await expect(page.locator('#apiServerInput')).toHaveValue(endorserServer); }); -test('Check User 0 can register a random person', async ({ page }) => { +test('Check invalid DID shows error and redirects', async ({ page }) => { await importUser(page, '00'); - const newDid = await generateAndRegisterEthrUser(page); - expect(newDid).toContain('did:ethr:'); + + // Navigate to an invalid DID URL + await page.goto('./did/0'); + + // Should show error message about invalid DID format + await expect(page.getByText(NOTIFY_CONTACT_INVALID_DID.message)).toBeVisible(); + + // Should redirect to homepage + await expect(page).toHaveURL(/.*\/$/); +}); - await page.goto('./'); - await page.getByTestId('closeOnboardingAndFinish').click(); - await page.getByRole('button', { name: 'Person' }).click(); - await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click(); - await page.getByPlaceholder('What was given').fill('Gave me access!'); - await page.getByRole('button', { name: 'Sign & Send' }).click(); - await expect(page.getByText('That gift was recorded.')).toBeVisible(); - // now ensure that alert goes away - await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss alert - await expect(page.getByText('That gift was recorded.')).toBeHidden(); - - // now delete the contact to test that pages still do reasonable things - await deleteContact(page, newDid); - // go the activity page for this new person - await page.goto('./did/' + encodeURIComponent(newDid)); - // maybe replace by: const popupPromise = page.waitForEvent('popup'); - let error; - try { - await page.waitForSelector('div[role="alert"]', { timeout: 2000 }); - error = new Error('Error alert should not show.'); - } catch (error) { - // success - } finally { - if (error) { - throw error; - } - } +test('Check User 0 can register a random person', async ({ page }) => { + const newDid = await generateNewEthrUser(page); // generate a new user + + await importUserFromAccount(page, "00"); // switch to User Zero + + // As User Zero, add the new user as a contact + await page.goto('./contacts'); + const contactName = createContactName(newDid); + await page.getByPlaceholder('URL or DID, Name, Public Key').fill(`${newDid}, ${contactName}`); + await expect(page.locator('button > svg.fa-plus')).toBeVisible(); + await page.locator('button > svg.fa-plus').click(); + await expect(page.locator('div[role="alert"] h4:has-text("Success")')).toBeVisible(); // wait for info alert to be visible… + await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // …and dismiss it + await expect(page.locator('div[role="alert"] button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone + await page.locator('div[role="alert"] button:text-is("Yes")').click(); // Register new contact + await page.locator('div[role="alert"] button:text-is("No, Not Now")').click(); // Dismiss export data prompt + await expect(page.locator("li", { hasText: contactName })).toBeVisible(); }); diff --git a/test-playwright/03-duplicate-import-test.spec.ts b/test-playwright/03-duplicate-import-test.spec.ts new file mode 100644 index 00000000..0bdc80bd --- /dev/null +++ b/test-playwright/03-duplicate-import-test.spec.ts @@ -0,0 +1,63 @@ +import { test, expect } from '@playwright/test'; +import { importUserFromAccount, getTestUserData } from './testUtils'; +import { NOTIFY_DUPLICATE_ACCOUNT_IMPORT } from '../src/constants/notifications'; + +/** + * Test duplicate account import functionality + * + * This test verifies that: + * 1. A user can successfully import an account the first time + * 2. Attempting to import the same account again shows a warning message + * 3. The duplicate import is prevented + */ +test.describe('Duplicate Account Import', () => { + test('should prevent importing the same account twice', async ({ page }) => { + const userData = getTestUserData("00"); + + // First import - should succeed + await page.goto("./start"); + await page.getByText("You have a seed").click(); + await page.getByPlaceholder("Seed Phrase").fill(userData.seedPhrase); + await page.getByRole("button", { name: "Import" }).click(); + + // Verify first import was successful + await expect(page.getByRole("code")).toContainText(userData.did); + + // Navigate back to start page for second import attempt + await page.goto("./start"); + await page.getByText("You have a seed").click(); + await page.getByPlaceholder("Seed Phrase").fill(userData.seedPhrase); + await page.getByRole("button", { name: "Import" }).click(); + + // Verify duplicate import shows warning message + // The warning can appear either from the pre-check or from the saveNewIdentity error handling + await expect(page.getByText(NOTIFY_DUPLICATE_ACCOUNT_IMPORT.message)).toBeVisible(); + + // Verify we're still on the import page (not redirected to account) + await expect(page.getByPlaceholder("Seed Phrase")).toBeVisible(); + }); + + test('should allow importing different accounts', async ({ page }) => { + const userZeroData = getTestUserData("00"); + const userOneData = getTestUserData("01"); + + // Import first user + await page.goto("./start"); + await page.getByText("You have a seed").click(); + await page.getByPlaceholder("Seed Phrase").fill(userZeroData.seedPhrase); + await page.getByRole("button", { name: "Import" }).click(); + + // Verify first import was successful + await expect(page.getByRole("code")).toContainText(userZeroData.did); + + // Navigate back to start page for second user import + await page.goto("./start"); + await page.getByText("You have a seed").click(); + await page.getByPlaceholder("Seed Phrase").fill(userOneData.seedPhrase); + await page.getByRole("button", { name: "Import" }).click(); + + // Verify second import was successful (should not show duplicate warning) + await expect(page.getByRole("code")).toContainText(userOneData.did); + await expect(page.getByText(NOTIFY_DUPLICATE_ACCOUNT_IMPORT.message)).not.toBeVisible(); + }); +}); diff --git a/test-playwright/05-invite.spec.ts b/test-playwright/05-invite.spec.ts index 821f7c39..d0cf5b19 100644 --- a/test-playwright/05-invite.spec.ts +++ b/test-playwright/05-invite.spec.ts @@ -8,6 +8,7 @@ * - Custom expiration date * 2. The invitation appears in the list after creation * 3. A new user can accept the invitation and become connected + * 4. The new user can create gift records from the front page * * Test Flow: * 1. Imports User 0 (test account) @@ -19,6 +20,8 @@ * 4. Creates a new user with Ethr DID * 5. Accepts the invitation as the new user * 6. Verifies the connection is established + * 7. Tests that the new user can create gift records from the front page + * 8. Verifies the gift appears in the home view * * Related Files: * - Frontend invite handling: src/libs/endorserServer.ts @@ -29,7 +32,7 @@ * @requires ./testUtils - For user management utilities */ import { test, expect } from '@playwright/test'; -import { deleteContact, generateNewEthrUser, generateRandomString, importUser, switchToUser } from './testUtils'; +import { createGiftFromFrontPageForNewUser, deleteContact, generateNewEthrUser, generateRandomString, importUser, switchToUser } from './testUtils'; test('Check User 0 can invite someone', async ({ page }) => { await importUser(page, '00'); @@ -58,4 +61,7 @@ test('Check User 0 can invite someone', async ({ page }) => { await page.locator('button:has-text("Save")').click(); await expect(page.locator('button:has-text("Save")')).toBeHidden(); await expect(page.locator(`li:has-text("My pal User #0")`)).toBeVisible(); + + // Verify the new user can create a gift record from the front page + const giftTitle = await createGiftFromFrontPageForNewUser(page, `Gift from new user ${neighborNum}`); }); diff --git a/test-playwright/30-record-gift.spec.ts b/test-playwright/30-record-gift.spec.ts index d8ee9698..cd942236 100644 --- a/test-playwright/30-record-gift.spec.ts +++ b/test-playwright/30-record-gift.spec.ts @@ -79,6 +79,7 @@ * ``` */ import { test, expect } from '@playwright/test'; +import { UNNAMED_ENTITY_NAME } from '../src/constants/entities'; import { importUser } from './testUtils'; test('Record something given', async ({ page }) => { @@ -101,7 +102,7 @@ test('Record something given', async ({ page }) => { await page.goto('./'); await page.getByTestId('closeOnboardingAndFinish').click(); await page.getByRole('button', { name: 'Person' }).click(); - await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click(); + await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click(); await page.getByPlaceholder('What was given').fill(finalTitle); await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString()); await page.getByRole('button', { name: 'Sign & Send' }).click(); diff --git a/test-playwright/33-record-gift-x10.spec.ts b/test-playwright/33-record-gift-x10.spec.ts index f0dfeef4..37c5eb02 100644 --- a/test-playwright/33-record-gift-x10.spec.ts +++ b/test-playwright/33-record-gift-x10.spec.ts @@ -85,6 +85,7 @@ */ import { test, expect } from '@playwright/test'; +import { UNNAMED_ENTITY_NAME } from '../src/constants/entities'; import { importUser, createUniqueStringsArray, createRandomNumbersArray } from './testUtils'; test('Record 9 new gifts', async ({ page }) => { @@ -116,7 +117,7 @@ test('Record 9 new gifts', async ({ page }) => { await page.getByTestId('closeOnboardingAndFinish').click(); } await page.getByRole('button', { name: 'Person' }).click(); - await page.getByRole('listitem').filter({ hasText: 'Unnamed' }).locator('svg').click(); + await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click(); await page.getByPlaceholder('What was given').fill(finalTitles[i]); await page.getByRole('spinbutton').fill(finalNumbers[i].toString()); await page.getByRole('button', { name: 'Sign & Send' }).click(); diff --git a/test-playwright/50-record-offer.spec.ts b/test-playwright/50-record-offer.spec.ts index f8065498..25134370 100644 --- a/test-playwright/50-record-offer.spec.ts +++ b/test-playwright/50-record-offer.spec.ts @@ -23,7 +23,7 @@ test('Record an offer', async ({ page }) => { await page.locator('button', { hasText: 'Edit' }).isVisible(); // since the 'edit' takes longer to show, wait for that (lest the click miss) await page.getByTestId('offerButton').click(); await page.getByTestId('inputDescription').fill(description); - await page.getByTestId('inputOfferAmount').fill(randomNonZeroNumber.toString()); + await page.getByTestId('inputOfferAmount').locator('input').fill(randomNonZeroNumber.toString()); expect(page.getByRole('button', { name: 'Sign & Send' })); await page.getByRole('button', { name: 'Sign & Send' }).click(); await expect(page.getByText('That offer was recorded.')).toBeVisible(); diff --git a/test-playwright/60-new-activity.spec.ts b/test-playwright/60-new-activity.spec.ts index daffbb30..23171c63 100644 --- a/test-playwright/60-new-activity.spec.ts +++ b/test-playwright/60-new-activity.spec.ts @@ -23,10 +23,11 @@ test('New offers for another user', async ({ page }) => { await page.getByPlaceholder('URL or DID, Name, Public Key').fill(autoCreatedDid + ', A Friend'); await expect(page.locator('button > svg.fa-plus')).toBeVisible(); await page.locator('button > svg.fa-plus').click(); - await page.locator('div[role="alert"] button:has-text("No")').click(); // don't register - await expect(page.locator('div[role="alert"] span:has-text("Success")')).toBeVisible(); - await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert + await expect(page.locator('div[role="alert"] h4:has-text("Success")')).toBeVisible(); // wait for info alert to be visible… + await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // …and dismiss it await expect(page.locator('div[role="alert"] button > svg.fa-xmark')).toBeHidden(); // ensure alert is gone + await page.locator('div[role="alert"] button:text-is("No")').click(); // Dismiss register prompt + await page.locator('div[role="alert"] button:text-is("No, Not Now")').click(); // Dismiss export data prompt // show buttons to make offers directly to people await page.getByRole('button').filter({ hasText: /See Actions/i }).click(); @@ -36,7 +37,7 @@ test('New offers for another user', async ({ page }) => { const randomString1 = Math.random().toString(36).substring(2, 5); await page.getByTestId('offerButton').click(); await page.getByTestId('inputDescription').fill(`help of ${randomString1} from #000`); - await page.getByTestId('inputOfferAmount').fill('1'); + await page.getByTestId('inputOfferAmount').locator('input').fill('1'); await page.getByRole('button', { name: 'Sign & Send' }).click(); await expect(page.getByText('That offer was recorded.')).toBeVisible(); await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert @@ -46,7 +47,7 @@ test('New offers for another user', async ({ page }) => { const randomString2 = Math.random().toString(36).substring(2, 5); await page.getByTestId('offerButton').click(); await page.getByTestId('inputDescription').fill(`help of ${randomString2} from #000`); - await page.getByTestId('inputOfferAmount').fill('3'); + await page.getByTestId('inputOfferAmount').locator('input').fill('3'); await page.getByRole('button', { name: 'Sign & Send' }).click(); await expect(page.getByText('That offer was recorded.')).toBeVisible(); await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert diff --git a/test-playwright/README.md b/test-playwright/README.md index 38ff5abf..b0a403fa 100644 --- a/test-playwright/README.md +++ b/test-playwright/README.md @@ -29,6 +29,7 @@ Tests are organized by feature area and numbered for execution order: ## Prerequisites 1. Endorser server running locally (see TESTING.md for setup) + ```bash git clone https://github.com/time-endorser/endorser-ch.git cd endorser-ch @@ -37,10 +38,13 @@ Tests are organized by feature area and numbered for execution order: cp .env.local .env NODE_ENV=test-local npm run dev ``` + 2. Playwright browsers installed: + ```bash npx playwright install ``` + 3. For mobile testing: - XCode (for iOS) - Android Studio or connected Android device @@ -67,16 +71,19 @@ npx playwright test -c playwright.config-local.ts --trace on test-playwright/40- ### Test Environment Options 1. Local Endorser Server (default): + ```bash NODE_ENV=test-local npm run dev ``` 2. Global Test Server: + ```bash VITE_DEFAULT_ENDORSER_API_SERVER=https://test-ledger.time.com npm run dev ``` 3. Minimal Test Data: + ```bash rm ../endorser-ch-test-local.sqlite3 NODE_ENV=test-local npm run flyway migrate @@ -114,6 +121,7 @@ For more detailed troubleshooting, see TESTING.md. ## Contributing When adding new tests: + 1. Follow the existing naming convention 2. Use testUtils.ts for common operations 3. Add appropriate comments and documentation @@ -124,4 +132,4 @@ When adding new tests: - [TESTING.md](./TESTING.md) - Detailed testing guide - [Playwright Documentation](https://playwright.dev/docs/intro) -- Endorser server documentation for test setup \ No newline at end of file +- Endorser server documentation for test setup diff --git a/test-playwright/TESTING.md b/test-playwright/TESTING.md index 7a173c65..3174c895 100644 --- a/test-playwright/TESTING.md +++ b/test-playwright/TESTING.md @@ -5,6 +5,7 @@ Start with [README.md](./README.md). This file has more details. ## Test User Setup ### Register New User on Test Server + On the test server, User #0 has rights to register others. Import User #0 with this seed phrase: ```bash @@ -18,6 +19,7 @@ This corresponds to: `did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F` ## Manual Testing Steps ### Identity Management + 1. Create multiple identifiers: - Go to "Your Identity" screen - Click "Advanced" @@ -96,10 +98,10 @@ npx playwright test -c playwright.config-local.ts test-playwright/60-new-activit ``` This command allows you to: + - **Run a specific test file**: `test-playwright/60-new-activity.spec.ts` - **Filter to a specific test**: `--grep "New offers for another user"` runs only tests with that name - **See the browser**: `--headed` opens the browser window so you can watch the test execute - **Use local config**: `-c playwright.config-local.ts` uses the local configuration file This is useful when you want to observe the testing process visually rather than running tests in headless mode. It's particularly helpful for debugging test failures or understanding how the application behaves during automated testing. - diff --git a/test-playwright/testUtils.ts b/test-playwright/testUtils.ts index 71df89f6..d0128a4e 100644 --- a/test-playwright/testUtils.ts +++ b/test-playwright/testUtils.ts @@ -1,4 +1,5 @@ import { expect, Page } from "@playwright/test"; +import { UNNAMED_ENTITY_NAME } from '../src/constants/entities'; // Get test user data based on the ID. // '01' -> user 111 @@ -109,7 +110,7 @@ export async function switchToUser(page: Page, did: string): Promise<void> { await page.getByTestId("didWrapper").locator('code:has-text("did:")'); } -function createContactName(did: string): string { +export function createContactName(did: string): string { return "User " + did.slice(11, 14); } @@ -144,30 +145,6 @@ export async function generateNewEthrUser(page: Page): Promise<string> { return newDid; } -// Generate a new random user and register them. -// Note that this makes 000 the active user. Use switchToUser to switch to this DID. -export async function generateAndRegisterEthrUser(page: Page): Promise<string> { - const newDid = await generateNewEthrUser(page); - - await importUser(page, "000"); // switch to user 000 - - await page.goto("./contacts"); - const contactName = createContactName(newDid); - await page - .getByPlaceholder("URL or DID, Name, Public Key") - .fill(`${newDid}, ${contactName}`); - await page.locator("button > svg.fa-plus").click(); - // register them - await page.locator('div[role="alert"] button:has-text("Yes")').click(); - // wait for it to disappear because the next steps may depend on alerts being gone - await expect( - page.locator('div[role="alert"] button:has-text("Yes")') - ).toBeHidden(); - await expect(page.locator("li", { hasText: contactName })).toBeVisible(); - - return newDid; -} - // Function to generate a random string of specified length export async function generateRandomString(length: number): Promise<string> { return Math.random() @@ -239,3 +216,44 @@ export function isResourceIntensiveTest(testPath: string): boolean { testPath.includes("40-add-contact") ); } + +/** + * Create a gift record from the front page + * @param page - Playwright page object + * @param giftTitle - Optional custom title, defaults to "Gift " + random string + * @param amount - Optional amount, defaults to random 1-99 + * @returns Promise resolving to the created gift title + */ +export async function createGiftFromFrontPageForNewUser( + page: Page, + giftTitle?: string, + amount?: number +): Promise<void> { + // Generate random values if not provided + const randomString = Math.random().toString(36).substring(2, 6); + const finalTitle = giftTitle || `Gift ${randomString}`; + const finalAmount = amount || Math.floor(Math.random() * 99) + 1; + + // Navigate to home page and close onboarding + await page.goto('./'); + await page.getByTestId('closeOnboardingAndFinish').click(); + + // Start gift creation flow + await page.getByRole('button', { name: 'Person' }).click(); + await page.getByRole('listitem').filter({ hasText: UNNAMED_ENTITY_NAME }).locator('svg').click(); + + // Fill gift details + await page.getByPlaceholder('What was given').fill(finalTitle); + await page.getByRole('spinbutton').fill(finalAmount.toString()); + + // Submit gift + await page.getByRole('button', { name: 'Sign & Send' }).click(); + + // Verify success + await expect(page.getByText('That gift was recorded.')).toBeVisible(); + await page.locator('div[role="alert"] button > svg.fa-xmark').click(); // dismiss info alert + + // Verify the gift appears in the home view + await page.goto('./'); + await expect(page.locator('ul#listLatestActivity li').filter({ hasText: giftTitle })).toBeVisible(); +} diff --git a/vite.config.common.mts b/vite.config.common.mts index f34e26d8..1736e288 100644 --- a/vite.config.common.mts +++ b/vite.config.common.mts @@ -6,10 +6,8 @@ import path from "path"; import { fileURLToPath } from 'url'; // Load environment variables -console.log('NODE_ENV:', process.env.NODE_ENV) dotenv.config({ path: `.env.${process.env.NODE_ENV}` }) - const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -37,9 +35,6 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> { assetsDir: 'assets', chunkSizeWarningLimit: 1000, rollupOptions: { - external: isNative - ? ['@capacitor/app'] - : [], output: { format: 'esm', generatedCode: { @@ -53,6 +48,22 @@ export async function createBuildConfig(platform: string): Promise<UserConfig> { format: 'es', plugins: () => [] }, + // ESBuild configuration to fail on errors - TEMPORARILY DISABLED + // esbuild: { + // target: 'es2015', + // supported: { + // 'bigint': true + // }, + // // Fail on any ESBuild errors + // logLevel: 'error', + // // Ensure build fails on syntax errors and other critical issues + // logOverride: { + // 'duplicate-export': 'error', + // 'duplicate-member': 'error', + // 'syntax-error': 'error', + // 'invalid-identifier': 'error' + // } + // }, define: { 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), diff --git a/vite.config.optimized.mts b/vite.config.optimized.mts index 5005ac5a..e9b4ee1d 100644 --- a/vite.config.optimized.mts +++ b/vite.config.optimized.mts @@ -135,7 +135,11 @@ export async function createOptimizedBuildConfig(mode: string): Promise<UserConf target: 'es2015', supported: { 'bigint': true - } + }, + // Fail on any ESBuild errors + logLevel: 'error', + // Ensure build fails on syntax errors + logOverride: { 'duplicate-export': 'error' } } }; } diff --git a/vite.config.utils.mts b/vite.config.utils.mts index b5a1ed97..238db91c 100644 --- a/vite.config.utils.mts +++ b/vite.config.utils.mts @@ -112,4 +112,28 @@ export async function loadAppConfig(): Promise<AppConfig> { "dexie-export-import/dist/import/index.js", }, }; +} + +/** + * Shared ESBuild configuration that ensures builds fail on errors + */ +export function getStrictESBuildConfig() { + return { + target: 'es2015', + supported: { + 'bigint': true + }, + // Fail on any ESBuild errors + logLevel: 'error' as const, + // Ensure build fails on syntax errors and other critical issues + logOverride: { + 'duplicate-export': 'error', + 'duplicate-member': 'error', + 'syntax-error': 'error', + 'invalid-identifier': 'error' + }, + // Additional strict settings + keepNames: false, + minifyIdentifiers: false + }; } \ No newline at end of file diff --git a/vite.config.web.mts b/vite.config.web.mts index 79f5ec9c..3acca4f4 100644 --- a/vite.config.web.mts +++ b/vite.config.web.mts @@ -1,99 +1,4 @@ -import { defineConfig, mergeConfig } from "vite"; +import { defineConfig } from "vite"; import { createBuildConfig } from "./vite.config.common.mts"; -import { loadAppConfig } from "./vite.config.utils.mts"; -export default defineConfig(async ({ mode }) => { - const baseConfig = await createBuildConfig('web'); - const appConfig = await loadAppConfig(); - - // Environment-specific configuration based on mode - const getEnvironmentConfig = (mode: string) => { - switch (mode) { - case 'production': - return { - // Production optimizations - build: { - minify: 'terser', - sourcemap: false, - rollupOptions: { - output: { - manualChunks: { - vendor: ['vue', 'vue-router', 'pinia'], - utils: ['luxon', 'ramda', 'zod'], - crypto: ['@ethersproject/wallet', '@ethersproject/hdnode', 'ethereum-cryptography'], - sql: ['@jlongster/sql.js', 'absurd-sql'] - } - } - } - }, - define: { - __DEV__: false, - __TEST__: false, - __PROD__: true - } - }; - case 'test': - return { - // Test environment configuration - build: { - minify: false, - sourcemap: true, - rollupOptions: { - output: { - manualChunks: undefined - } - } - }, - define: { - __DEV__: false, - __TEST__: true, - __PROD__: false - } - }; - default: // development - return { - // Development configuration - build: { - minify: false, - sourcemap: true, - rollupOptions: { - output: { - manualChunks: undefined - } - } - }, - define: { - __DEV__: true, - __TEST__: false, - __PROD__: false - } - }; - } - }; - - const environmentConfig = getEnvironmentConfig(mode); - - return mergeConfig(baseConfig, { - ...environmentConfig, - // Ensure source maps are enabled for development and test modes - // This affects both dev server and build output - sourcemap: mode === 'development' || mode === 'test', - // Server configuration inherited from base config - // CORS headers removed to allow images from any domain - plugins: [], - // Worker configuration for SQL worker - worker: { - format: 'es', - plugins: () => [] - }, - // Optimize dependencies for SQL worker - optimizeDeps: { - include: [ - '@jlongster/sql.js', - 'absurd-sql', - 'absurd-sql/dist/indexeddb-main-thread', - 'absurd-sql/dist/indexeddb-backend' - ] - } - }); -}); +export default defineConfig(async () => createBuildConfig('web'));