moai-adk 0.34.0__py3-none-any.whl → 1.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- moai_adk/__main__.py +136 -5
- moai_adk/astgrep/__init__.py +37 -0
- moai_adk/astgrep/analyzer.py +522 -0
- moai_adk/astgrep/models.py +124 -0
- moai_adk/astgrep/rules.py +179 -0
- moai_adk/cli/commands/analyze.py +11 -2
- moai_adk/cli/commands/doctor.py +7 -1
- moai_adk/cli/commands/init.py +321 -11
- moai_adk/cli/commands/language.py +7 -1
- moai_adk/cli/commands/rank.py +449 -0
- moai_adk/cli/commands/status.py +7 -1
- moai_adk/cli/commands/switch.py +325 -0
- moai_adk/cli/commands/update.py +296 -23
- moai_adk/cli/prompts/init_prompts.py +362 -66
- moai_adk/cli/prompts/translations/__init__.py +573 -0
- moai_adk/cli/ui/prompts.py +61 -2
- moai_adk/cli/worktree/cli.py +106 -1
- moai_adk/cli/worktree/manager.py +155 -0
- moai_adk/core/config/unified.py +244 -63
- moai_adk/core/credentials.py +264 -0
- moai_adk/core/error_recovery_system.py +22 -4
- moai_adk/core/git/conflict_detector.py +10 -1
- moai_adk/core/git/event_detector.py +16 -5
- moai_adk/core/integration/engine.py +2 -2
- moai_adk/core/integration/integration_tester.py +5 -5
- moai_adk/core/language_config_resolver.py +9 -3
- moai_adk/core/merge/analyzer.py +509 -324
- moai_adk/core/migration/alfred_to_moai_migrator.py +7 -1
- moai_adk/core/migration/backup_manager.py +54 -4
- moai_adk/core/migration/file_migrator.py +174 -2
- moai_adk/core/migration/interactive_checkbox_ui.py +42 -31
- moai_adk/core/migration/version_detector.py +123 -19
- moai_adk/core/migration/version_migrator.py +44 -9
- moai_adk/core/model_allocator.py +241 -0
- moai_adk/core/project/backup_utils.py +12 -2
- moai_adk/core/project/initializer.py +44 -87
- moai_adk/core/project/phase_executor.py +95 -33
- moai_adk/core/project/validator.py +16 -1
- moai_adk/core/quality/trust_checker.py +30 -10
- moai_adk/core/rollback_manager.py +60 -25
- moai_adk/core/template/backup.py +88 -6
- moai_adk/core/template/config.py +33 -9
- moai_adk/core/template/merger.py +34 -8
- moai_adk/core/template/processor.py +334 -11
- moai_adk/core/template_engine.py +10 -1
- moai_adk/core/template_variable_synchronizer.py +16 -2
- moai_adk/core/version_sync.py +54 -6
- moai_adk/foundation/__init__.py +1 -20
- moai_adk/foundation/testing.py +1 -1
- moai_adk/loop/__init__.py +54 -0
- moai_adk/loop/controller.py +305 -0
- moai_adk/loop/feedback.py +230 -0
- moai_adk/loop/state.py +209 -0
- moai_adk/loop/storage.py +220 -0
- moai_adk/lsp/__init__.py +70 -0
- moai_adk/lsp/client.py +320 -0
- moai_adk/lsp/models.py +261 -0
- moai_adk/lsp/protocol.py +404 -0
- moai_adk/lsp/server_manager.py +248 -0
- moai_adk/project/configuration.py +8 -1
- moai_adk/py.typed +0 -0
- moai_adk/ralph/__init__.py +37 -0
- moai_adk/ralph/engine.py +307 -0
- moai_adk/rank/__init__.py +21 -0
- moai_adk/rank/auth.py +425 -0
- moai_adk/rank/client.py +557 -0
- moai_adk/rank/config.py +147 -0
- moai_adk/rank/hook.py +1503 -0
- moai_adk/rank/py.typed +0 -0
- moai_adk/statusline/__init__.py +3 -0
- moai_adk/statusline/enhanced_output_style_detector.py +5 -5
- moai_adk/statusline/main.py +20 -1
- moai_adk/statusline/memory_collector.py +268 -0
- moai_adk/statusline/renderer.py +54 -38
- moai_adk/tag_system/__init__.py +48 -0
- moai_adk/tag_system/atomic_ops.py +117 -0
- moai_adk/tag_system/linkage.py +335 -0
- moai_adk/tag_system/parser.py +176 -0
- moai_adk/tag_system/validator.py +200 -0
- moai_adk/templates/.claude/agents/moai/builder-agent.md +19 -3
- moai_adk/templates/.claude/agents/moai/builder-command.md +62 -16
- moai_adk/templates/.claude/agents/moai/builder-plugin.md +763 -0
- moai_adk/templates/.claude/agents/moai/builder-skill.md +21 -5
- moai_adk/templates/.claude/agents/moai/expert-backend.md +103 -39
- moai_adk/templates/.claude/agents/moai/expert-debug.md +9 -3
- moai_adk/templates/.claude/agents/moai/expert-devops.md +16 -14
- moai_adk/templates/.claude/agents/moai/expert-frontend.md +45 -31
- moai_adk/templates/.claude/agents/moai/expert-performance.md +13 -9
- moai_adk/templates/.claude/agents/moai/expert-refactoring.md +228 -0
- moai_adk/templates/.claude/agents/moai/expert-security.md +19 -3
- moai_adk/templates/.claude/agents/moai/expert-testing.md +13 -9
- moai_adk/templates/.claude/agents/moai/manager-claude-code.md +8 -2
- moai_adk/templates/.claude/agents/moai/manager-docs.md +10 -5
- moai_adk/templates/.claude/agents/moai/manager-git.md +99 -27
- moai_adk/templates/.claude/agents/moai/manager-project.md +87 -7
- moai_adk/templates/.claude/agents/moai/manager-quality.md +22 -5
- moai_adk/templates/.claude/agents/moai/manager-spec.md +8 -2
- moai_adk/templates/.claude/agents/moai/manager-strategy.md +45 -14
- moai_adk/templates/.claude/agents/moai/manager-tdd.md +16 -3
- moai_adk/templates/.claude/commands/moai/0-project.md +239 -1185
- moai_adk/templates/.claude/commands/moai/1-plan.md +383 -363
- moai_adk/templates/.claude/commands/moai/2-run.md +254 -347
- moai_adk/templates/.claude/commands/moai/3-sync.md +174 -100
- moai_adk/templates/.claude/commands/moai/9-feedback.md +49 -33
- moai_adk/templates/.claude/commands/moai/alfred.md +339 -0
- moai_adk/templates/.claude/commands/moai/cancel-loop.md +163 -0
- moai_adk/templates/.claude/commands/moai/fix.md +264 -0
- moai_adk/templates/.claude/commands/moai/loop.md +363 -0
- moai_adk/templates/.claude/hooks/moai/lib/README.md +143 -0
- moai_adk/templates/.claude/hooks/moai/lib/__init__.py +37 -81
- moai_adk/templates/.claude/hooks/moai/lib/alfred_detector.py +105 -0
- moai_adk/templates/.claude/hooks/moai/lib/atomic_write.py +122 -0
- moai_adk/templates/.claude/hooks/moai/lib/checkpoint.py +4 -1
- moai_adk/templates/.claude/hooks/moai/lib/common.py +35 -5
- moai_adk/templates/.claude/hooks/moai/lib/config.py +376 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +24 -28
- moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +14 -14
- moai_adk/templates/.claude/hooks/moai/lib/enhanced_output_style_detector.py +372 -0
- moai_adk/templates/.claude/hooks/moai/lib/exceptions.py +171 -0
- moai_adk/templates/.claude/hooks/moai/lib/file_utils.py +95 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_collector.py +190 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +15 -13
- moai_adk/templates/.claude/hooks/moai/lib/language_detector.py +298 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +125 -25
- moai_adk/templates/.claude/hooks/moai/lib/main.py +341 -0
- moai_adk/templates/.claude/hooks/moai/lib/memory_collector.py +268 -0
- moai_adk/templates/.claude/hooks/moai/lib/metrics_tracker.py +78 -0
- moai_adk/templates/.claude/hooks/moai/lib/models.py +9 -7
- moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +204 -13
- moai_adk/templates/.claude/hooks/moai/lib/project.py +23 -14
- moai_adk/templates/.claude/hooks/moai/lib/renderer.py +359 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_linkage.py +333 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_parser.py +176 -0
- moai_adk/templates/.claude/hooks/moai/lib/tag_validator.py +200 -0
- moai_adk/templates/.claude/hooks/moai/lib/timeout.py +5 -5
- moai_adk/templates/.claude/hooks/moai/lib/tool_registry.py +896 -0
- moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +30 -18
- moai_adk/templates/.claude/hooks/moai/lib/update_checker.py +129 -0
- moai_adk/templates/.claude/hooks/moai/lib/version_reader.py +741 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__ast_grep_scan.py +276 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__code_formatter.py +255 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__coverage_guard.py +325 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__linter.py +315 -0
- moai_adk/templates/.claude/hooks/moai/post_tool__lsp_diagnostic.py +508 -0
- moai_adk/templates/.claude/hooks/moai/pre_commit__tag_validator.py +287 -0
- moai_adk/templates/.claude/hooks/moai/pre_tool__security_guard.py +268 -0
- moai_adk/templates/.claude/hooks/moai/pre_tool__tdd_enforcer.py +208 -0
- moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +93 -61
- moai_adk/templates/.claude/hooks/moai/session_end__rank_submit.py +69 -0
- moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +165 -70
- moai_adk/templates/.claude/hooks/moai/shared/utils/announcement_translator.py +206 -0
- moai_adk/templates/.claude/hooks/moai/stop__loop_controller.py +621 -0
- moai_adk/templates/.claude/output-styles/moai/alfred.md +758 -0
- moai_adk/templates/.claude/output-styles/moai/r2d2.md +86 -3
- moai_adk/templates/.claude/output-styles/moai/yoda.md +2 -2
- moai_adk/templates/.claude/settings.json +154 -77
- moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +252 -198
- moai_adk/templates/.claude/skills/moai-docs-generation/examples.md +169 -323
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +39 -27
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +115 -125
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +150 -150
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +182 -175
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +198 -138
- moai_adk/templates/.claude/skills/moai-docs-generation/reference.md +226 -320
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +43 -222
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +75 -219
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +103 -463
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/component-architecture.md +723 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/nextjs16-patterns.md +713 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/performance-optimization.md +694 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/react19-patterns.md +591 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/state-management.md +680 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/modules/vue35-patterns.md +802 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +118 -339
- moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +74 -377
- moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +299 -70
- moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +205 -182
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/advanced-agent-patterns.md +370 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-cli-reference-official.md +420 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +32 -22
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-devcontainers-official.md +381 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-discover-plugins-official.md +379 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-headless-official.md +378 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +110 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugin-marketplaces-official.md +308 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-plugins-official.md +640 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sandboxing-official.md +282 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +425 -71
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-statusline-official.md +293 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +325 -143
- moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +96 -316
- moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +116 -294
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-advanced.md +279 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-implementation.md +267 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +121 -650
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/patterns.md +22 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-ears-format.md +200 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +37 -730
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-tdd-implementation.md +275 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +77 -819
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-implementation.md +244 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-validation.md +219 -0
- moai_adk/templates/.claude/skills/moai-foundation-philosopher/SKILL.md +14 -18
- moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +86 -270
- moai_adk/templates/.claude/skills/moai-framework-electron/SKILL.md +288 -0
- moai_adk/templates/.claude/skills/moai-framework-electron/examples.md +2082 -0
- moai_adk/templates/.claude/skills/moai-framework-electron/reference.md +1649 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/SKILL.md +76 -582
- moai_adk/templates/.claude/skills/moai-lang-cpp/examples.md +1239 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/modules/advanced-patterns.md +401 -0
- moai_adk/templates/.claude/skills/moai-lang-cpp/reference.md +1136 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/SKILL.md +82 -436
- moai_adk/templates/.claude/skills/moai-lang-csharp/examples.md +585 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/aspnet-core.md +627 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/blazor-components.md +767 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/cqrs-validation.md +626 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/csharp12-features.md +580 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/modules/efcore-patterns.md +622 -0
- moai_adk/templates/.claude/skills/moai-lang-csharp/reference.md +403 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/SKILL.md +65 -542
- moai_adk/templates/.claude/skills/moai-lang-elixir/examples.md +1171 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/modules/advanced-patterns.md +531 -0
- moai_adk/templates/.claude/skills/moai-lang-elixir/reference.md +889 -0
- moai_adk/templates/.claude/skills/moai-lang-flutter/SKILL.md +32 -405
- moai_adk/templates/.claude/skills/moai-lang-go/SKILL.md +114 -293
- moai_adk/templates/.claude/skills/moai-lang-java/SKILL.md +83 -307
- moai_adk/templates/.claude/skills/moai-lang-javascript/SKILL.md +179 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/examples.md +973 -0
- moai_adk/templates/.claude/skills/moai-lang-javascript/reference.md +1543 -0
- moai_adk/templates/.claude/skills/moai-lang-kotlin/SKILL.md +42 -279
- moai_adk/templates/.claude/skills/moai-lang-php/SKILL.md +94 -556
- moai_adk/templates/.claude/skills/moai-lang-php/examples.md +1608 -0
- moai_adk/templates/.claude/skills/moai-lang-php/modules/advanced-patterns.md +538 -0
- moai_adk/templates/.claude/skills/moai-lang-php/reference.md +1323 -0
- moai_adk/templates/.claude/skills/moai-lang-python/SKILL.md +108 -358
- moai_adk/templates/.claude/skills/moai-lang-r/SKILL.md +84 -482
- moai_adk/templates/.claude/skills/moai-lang-r/examples.md +1154 -0
- moai_adk/templates/.claude/skills/moai-lang-r/modules/advanced-patterns.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-r/reference.md +1087 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/SKILL.md +106 -610
- moai_adk/templates/.claude/skills/moai-lang-ruby/examples.md +1106 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/advanced-patterns.md +309 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/modules/testing-patterns.md +306 -0
- moai_adk/templates/.claude/skills/moai-lang-ruby/reference.md +1024 -0
- moai_adk/templates/.claude/skills/moai-lang-rust/SKILL.md +51 -265
- moai_adk/templates/.claude/skills/moai-lang-scala/SKILL.md +106 -442
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/akka-actors.md +479 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/cats-effect.md +489 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/functional-programming.md +460 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/spark-data.md +498 -0
- moai_adk/templates/.claude/skills/moai-lang-scala/modules/zio-patterns.md +541 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/SKILL.md +88 -457
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/combine-reactive.md +256 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/concurrency.md +270 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swift6-features.md +265 -0
- moai_adk/templates/.claude/skills/moai-lang-swift/modules/swiftui-patterns.md +314 -0
- moai_adk/templates/.claude/skills/moai-lang-typescript/SKILL.md +75 -283
- moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +97 -252
- moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +64 -240
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +331 -12
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +330 -37
- moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +90 -287
- moai_adk/templates/.claude/skills/moai-platform-auth0/SKILL.md +200 -206
- moai_adk/templates/.claude/skills/moai-platform-auth0/examples.md +2446 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/adaptive-mfa.md +233 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/akamai-integration.md +214 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/application-credentials.md +280 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-log-events.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/attack-protection-overview.md +140 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/bot-detection.md +144 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/breached-password-detection.md +187 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/brute-force-protection.md +189 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/certifications.md +282 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/compliance-overview.md +263 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/continuous-session-protection.md +307 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/customize-mfa.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/dpop-implementation.md +283 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/fapi-implementation.md +259 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/gdpr-compliance.md +313 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/guardian-configuration.md +269 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/highly-regulated-identity.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/jwt-fundamentals.md +248 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mdl-verification.md +210 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-api-management.md +278 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-factors.md +226 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mfa-overview.md +174 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/mtls-sender-constraining.md +316 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/ropg-flow-mfa.md +216 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-center.md +325 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/security-guidance.md +277 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/state-parameters.md +177 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/step-up-authentication.md +251 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/suspicious-ip-throttling.md +240 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/tenant-access-control.md +179 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/modules/webauthn-fido.md +235 -0
- moai_adk/templates/.claude/skills/moai-platform-auth0/reference.md +224 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/SKILL.md +75 -330
- moai_adk/templates/.claude/skills/moai-platform-clerk/examples.md +1426 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/modules/advanced-patterns.md +417 -0
- moai_adk/templates/.claude/skills/moai-platform-clerk/reference.md +273 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/SKILL.md +100 -340
- moai_adk/templates/.claude/skills/moai-platform-convex/examples.md +506 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/auth-integration.md +421 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/file-storage.md +474 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/reactive-queries.md +302 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/modules/server-functions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-convex/reference.md +385 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/SKILL.md +113 -326
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/examples.md +514 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/custom-claims.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/phone-auth.md +372 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/modules/social-auth.md +339 -0
- moai_adk/templates/.claude/skills/moai-platform-firebase-auth/reference.md +382 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/SKILL.md +71 -302
- moai_adk/templates/.claude/skills/moai-platform-firestore/examples.md +445 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/offline-cache.md +392 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/realtime-listeners.md +441 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/security-rules.md +352 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/modules/transactions.md +452 -0
- moai_adk/templates/.claude/skills/moai-platform-firestore/reference.md +322 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/SKILL.md +101 -412
- moai_adk/templates/.claude/skills/moai-platform-neon/examples.md +470 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/auto-scaling.md +349 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/branching-workflows.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/connection-pooling.md +412 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/modules/pitr-backups.md +458 -0
- moai_adk/templates/.claude/skills/moai-platform-neon/reference.md +272 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/SKILL.md +96 -327
- moai_adk/templates/.claude/skills/moai-platform-railway/examples.md +539 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/docker-deployment.md +261 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/multi-service.md +291 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/networking-domains.md +338 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/modules/volumes-storage.md +353 -0
- moai_adk/templates/.claude/skills/moai-platform-railway/reference.md +374 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/SKILL.md +103 -428
- moai_adk/templates/.claude/skills/moai-platform-supabase/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/auth-integration.md +384 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/edge-functions.md +371 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/postgresql-pgvector.md +231 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/realtime-presence.md +354 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/row-level-security.md +286 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/storage-cdn.md +319 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/modules/typescript-patterns.md +453 -0
- moai_adk/templates/.claude/skills/moai-platform-supabase/reference.md +284 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/SKILL.md +96 -446
- moai_adk/templates/.claude/skills/moai-platform-vercel/examples.md +502 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/analytics-speed.md +348 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/deployment-config.md +344 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/edge-functions.md +222 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/isr-caching.md +306 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/modules/kv-storage.md +399 -0
- moai_adk/templates/.claude/skills/moai-platform-vercel/reference.md +360 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/SKILL.md +193 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/examples.md +1099 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/language-specific.md +307 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/pattern-syntax.md +237 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/refactoring-patterns.md +260 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/modules/security-rules.md +239 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/reference.md +288 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/go.yml +90 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/python.yml +101 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/languages/typescript.yml +83 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/complexity-check.yml +94 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/quality/deprecated-apis.yml +84 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/secrets-detection.yml +89 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/sql-injection.yml +45 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/security/xss-prevention.yml +50 -0
- moai_adk/templates/.claude/skills/moai-tool-ast-grep/rules/sgconfig.yml +54 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +225 -423
- moai_adk/templates/.claude/skills/moai-workflow-loop/SKILL.md +197 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/examples.md +1063 -0
- moai_adk/templates/.claude/skills/moai-workflow-loop/reference.md +1414 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +211 -314
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +15 -43
- moai_adk/templates/.claude/skills/moai-workflow-spec/SKILL.md +119 -316
- moai_adk/templates/.claude/skills/moai-workflow-spec/modules/advanced-patterns.md +237 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +96 -203
- moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +201 -388
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +52 -3
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +263 -806
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/context7-integration.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/review-workflows.md +500 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/relevance-analysis.md +154 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/safety-analysis.md +148 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/scoring-algorithms.md +196 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/timeliness-analysis.md +168 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/truthfulness-analysis.md +136 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework/usability-analysis.md +153 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review/trust5-framework.md +257 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +191 -1344
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/analysis-patterns.md +340 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/core-classes.md +299 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/code-review/tool-integration.md +380 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/debugging-workflows.md +451 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/debugging/error-analysis.md +442 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/optimization-patterns.md +473 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance/profiling-techniques.md +481 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/ai-optimization.md +241 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/bottleneck-detection.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/optimization-plan.md +315 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/profiler-core.md +277 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization/real-time-monitoring.md +187 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +287 -1194
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/quality-metrics.md +415 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/ai-workflows.md +620 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/refactoring/patterns.md +692 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/security-analysis.md +429 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +262 -1192
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/static-analysis.md +438 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd/core-classes.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/advanced-features.md +494 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/red-green-refactor.md +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-generation.md +471 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7/test-patterns.md +371 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +227 -1222
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/trust5-validation.md +428 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/SKILL.md +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/integration-patterns.md +149 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/moai-adk-integration.md +245 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-advanced.md +310 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-development.md +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/parallel-workflows.md +302 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/registry-architecture.md +271 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/resource-optimization.md +300 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/tools-integration.md +280 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/troubleshooting.md +397 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-commands.md +296 -0
- moai_adk/templates/.claude/skills/moai-workflow-worktree/modules/worktree-management.md +217 -0
- moai_adk/templates/.git-hooks/pre-push +162 -59
- moai_adk/templates/.github/workflows/ci-universal.yml +934 -133
- moai_adk/templates/.gitignore +65 -107
- moai_adk/templates/.lsp.json +152 -0
- moai_adk/templates/.mcp.json +2 -20
- moai_adk/templates/.moai/announcements/en.json +18 -0
- moai_adk/templates/.moai/announcements/ja.json +18 -0
- moai_adk/templates/.moai/announcements/ko.json +18 -0
- moai_adk/templates/.moai/announcements/zh.json +18 -0
- moai_adk/templates/.moai/config/config.yaml +8 -2
- moai_adk/templates/.moai/config/multilingual-triggers.yaml +213 -0
- moai_adk/templates/.moai/config/sections/language.yaml +2 -2
- moai_adk/templates/.moai/config/sections/llm.yaml +41 -0
- moai_adk/templates/.moai/config/sections/pricing.yaml +30 -0
- moai_adk/templates/.moai/config/sections/project.yaml +2 -2
- moai_adk/templates/.moai/config/sections/quality.yaml +43 -5
- moai_adk/templates/.moai/config/sections/ralph.yaml +55 -0
- moai_adk/templates/.moai/config/sections/system.yaml +46 -1
- moai_adk/templates/.moai/config/sections/user.yaml +1 -1
- moai_adk/templates/.moai/config/statusline-config.yaml +2 -2
- moai_adk/templates/.moai/llm-configs/glm.json +22 -0
- moai_adk/templates/CLAUDE.ja.md +343 -0
- moai_adk/templates/CLAUDE.ko.md +343 -0
- moai_adk/templates/CLAUDE.md +200 -499
- moai_adk/templates/CLAUDE.zh.md +343 -0
- moai_adk/utils/common.py +37 -0
- moai_adk/version.py +1 -1
- moai_adk-1.1.0.dist-info/METADATA +2443 -0
- moai_adk-1.1.0.dist-info/RECORD +701 -0
- {moai_adk-0.34.0.dist-info → moai_adk-1.1.0.dist-info}/entry_points.txt +2 -0
- moai_adk-1.1.0.dist-info/licenses/LICENSE +99 -0
- moai_adk/core/config/auto_spec_config.py +0 -340
- moai_adk/core/hooks/post_tool_auto_spec_completion.py +0 -901
- moai_adk/core/spec/confidence_scoring.py +0 -680
- moai_adk/core/spec/ears_template_engine.py +0 -1247
- moai_adk/core/spec/quality_validator.py +0 -687
- moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +0 -670
- moai_adk/templates/.claude/agents/moai/expert-database.md +0 -777
- moai_adk/templates/.claude/agents/moai/expert-uiux.md +0 -1041
- moai_adk/templates/.claude/agents/moai/mcp-context7.md +0 -458
- moai_adk/templates/.claude/agents/moai/mcp-figma.md +0 -1607
- moai_adk/templates/.claude/agents/moai/mcp-notion.md +0 -789
- moai_adk/templates/.claude/agents/moai/mcp-playwright.md +0 -469
- moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +0 -1032
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/SKILL.md +0 -438
- moai_adk/templates/.claude/skills/moai-ai-nano-banana/examples.md +0 -431
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/design-system-tokens.md +0 -405
- moai_adk/templates/.claude/skills/moai-library-nextra/advanced-patterns.md +0 -336
- moai_adk/templates/.claude/skills/moai-mcp-figma/SKILL.md +0 -402
- moai_adk/templates/.claude/skills/moai-mcp-figma/advanced-patterns.md +0 -607
- moai_adk/templates/.claude/skills/moai-mcp-notion/SKILL.md +0 -300
- moai_adk/templates/.claude/skills/moai-mcp-notion/advanced-patterns.md +0 -537
- moai_adk/templates/.claude/skills/moai-workflow-project/__init__.py +0 -520
- moai_adk/templates/.claude/skills/moai-workflow-project/complete_workflow_demo_fixed.py +0 -574
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_project_setup.py +0 -317
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_workflow_demo.py +0 -663
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/config-migration-example.json +0 -190
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/question-examples.json +0 -175
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/quick_start.py +0 -196
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/__init__.py +0 -17
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/advanced-patterns.md +0 -158
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/ask_user_integration.py +0 -340
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/batch_questions.py +0 -713
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/config_manager.py +0 -538
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/documentation_manager.py +0 -1336
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/language_initializer.py +0 -730
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/migration_manager.py +0 -608
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/template_optimizer.py +0 -1005
- moai_adk/templates/.claude/skills/moai-workflow-project/test_integration_simple.py +0 -436
- moai_adk/templates/.claude/skills/moai-worktree/SKILL.md +0 -411
- moai_adk/templates/.claude/skills/moai-worktree/modules/integration-patterns.md +0 -982
- moai_adk/templates/.claude/skills/moai-worktree/modules/parallel-development.md +0 -778
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-commands.md +0 -646
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-management.md +0 -782
- moai_adk/templates/.moai/config/questions/_schema.yaml +0 -151
- moai_adk/templates/.moai/config/questions/tab0-init.yaml +0 -251
- moai_adk/templates/.moai/config/questions/tab1-user.yaml +0 -108
- moai_adk/templates/.moai/config/questions/tab2-project.yaml +0 -81
- moai_adk/templates/.moai/config/questions/tab3-git.yaml +0 -634
- moai_adk/templates/.moai/config/questions/tab4-quality.yaml +0 -170
- moai_adk/templates/.moai/config/questions/tab5-system.yaml +0 -87
- moai_adk/templates/.moai/scripts/setup-glm.py +0 -136
- moai_adk-0.34.0.dist-info/METADATA +0 -2999
- moai_adk-0.34.0.dist-info/RECORD +0 -463
- moai_adk-0.34.0.dist-info/licenses/LICENSE +0 -21
- /moai_adk/foundation/{git.py → git/__init__.py} +0 -0
- /moai_adk/templates/.claude/skills/moai-library-mermaid/{advanced-patterns.md → modules/advanced-patterns.md} +0 -0
- /moai_adk/templates/.claude/skills/moai-library-mermaid/{optimization.md → modules/optimization.md} +0 -0
- /moai_adk/templates/.claude/skills/moai-library-nextra/{optimization.md → modules/optimization.md} +0 -0
- /moai_adk/templates/.claude/skills/moai-workflow-jit-docs/{advanced-patterns.md → modules/advanced-patterns.md} +0 -0
- /moai_adk/templates/.claude/skills/moai-workflow-jit-docs/{optimization.md → modules/optimization.md} +0 -0
- /moai_adk/templates/.claude/skills/moai-workflow-testing/{advanced-patterns.md → modules/advanced-patterns.md} +0 -0
- /moai_adk/templates/.claude/skills/moai-workflow-testing/{optimization.md → modules/optimization.md} +0 -0
- /moai_adk/templates/.claude/skills/{moai-worktree → moai-workflow-worktree}/examples.md +0 -0
- /moai_adk/templates/.claude/skills/{moai-worktree → moai-workflow-worktree}/reference.md +0 -0
- {moai_adk-0.34.0.dist-info → moai_adk-1.1.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,1649 @@
|
|
|
1
|
+
# Electron Framework Reference Guide
|
|
2
|
+
|
|
3
|
+
## Platform Version Matrix
|
|
4
|
+
|
|
5
|
+
### Electron 33 (October 2024) - Current Stable
|
|
6
|
+
|
|
7
|
+
- Chromium: 130
|
|
8
|
+
- Node.js: 20.18.0
|
|
9
|
+
- V8: 13.0
|
|
10
|
+
- Key Features:
|
|
11
|
+
- Enhanced security defaults with sandbox enabled by default
|
|
12
|
+
- Improved context isolation patterns
|
|
13
|
+
- Native ESM support in main process
|
|
14
|
+
- Service Worker support in renderer
|
|
15
|
+
- WebGPU API support for GPU-accelerated graphics
|
|
16
|
+
- Improved auto-updater with differential updates
|
|
17
|
+
- Utility process enhancements for background tasks
|
|
18
|
+
- Better crash reporting integration
|
|
19
|
+
|
|
20
|
+
### Electron 32 (August 2024)
|
|
21
|
+
|
|
22
|
+
- Chromium: 128
|
|
23
|
+
- Node.js: 20.16.0
|
|
24
|
+
- Key Features:
|
|
25
|
+
- Utility process improvements
|
|
26
|
+
- Enhanced file system access
|
|
27
|
+
- Better macOS notarization support
|
|
28
|
+
- Improved window management APIs
|
|
29
|
+
|
|
30
|
+
### Electron 31 (June 2024)
|
|
31
|
+
|
|
32
|
+
- Chromium: 126
|
|
33
|
+
- Node.js: 20.14.0
|
|
34
|
+
- Key Features:
|
|
35
|
+
- Performance improvements for large apps
|
|
36
|
+
- Enhanced IPC serialization
|
|
37
|
+
- Better TypeScript support
|
|
38
|
+
|
|
39
|
+
## Context7 Library Mappings
|
|
40
|
+
|
|
41
|
+
### Core Framework
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
/electron/electron - Electron framework
|
|
45
|
+
/electron/forge - Electron Forge tooling
|
|
46
|
+
/electron-userland/electron-builder - App packaging
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Build Tools
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
/nickmeinhold/electron-vite - Vite integration
|
|
53
|
+
/nickmeinhold/electron-esbuild - esbuild integration
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Native Modules
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
/nickmeinhold/better-sqlite3 - SQLite database
|
|
60
|
+
/nickmeinhold/keytar - Secure credential storage
|
|
61
|
+
/nickmeinhold/node-pty - Terminal emulation
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Auto-Update
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
/electron-userland/electron-updater - Auto-update support
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Testing
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
/nickmeinhold/spectron - E2E testing (deprecated)
|
|
74
|
+
/nickmeinhold/playwright - Modern E2E testing
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Architecture Patterns
|
|
80
|
+
|
|
81
|
+
### Process Model
|
|
82
|
+
|
|
83
|
+
Electron Process Architecture:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
┌─────────────────────────────────────┐
|
|
87
|
+
│ Main Process │
|
|
88
|
+
│ - Single instance per app │
|
|
89
|
+
│ - Full Node.js access │
|
|
90
|
+
│ - Creates BrowserWindows │
|
|
91
|
+
│ - Manages app lifecycle │
|
|
92
|
+
│ - Native OS integration │
|
|
93
|
+
└─────────────┬───────────────────────┘
|
|
94
|
+
│
|
|
95
|
+
┌─────────────────────┼─────────────────────┐
|
|
96
|
+
│ │ │
|
|
97
|
+
▼ ▼ ▼
|
|
98
|
+
┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐
|
|
99
|
+
│ Renderer Process │ │ Renderer Process │ │ Utility Process │
|
|
100
|
+
│ - Web content │ │ - Web content │ │ - Background │
|
|
101
|
+
│ - Sandboxed │ │ - Sandboxed │ │ tasks │
|
|
102
|
+
│ - No Node.js │ │ - No Node.js │ │ - Node.js access │
|
|
103
|
+
│ (default) │ │ (default) │ │ - No GUI │
|
|
104
|
+
└───────────────────┘ └───────────────────┘ └───────────────────┘
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Recommended Project Structure
|
|
108
|
+
|
|
109
|
+
Directory Layout:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
electron-app/
|
|
113
|
+
├── src/
|
|
114
|
+
│ ├── main/ # Main process code
|
|
115
|
+
│ │ ├── index.ts # Entry point
|
|
116
|
+
│ │ ├── app.ts # App lifecycle
|
|
117
|
+
│ │ ├── ipc/ # IPC handlers
|
|
118
|
+
│ │ │ ├── index.ts
|
|
119
|
+
│ │ │ ├── file-handlers.ts
|
|
120
|
+
│ │ │ └── window-handlers.ts
|
|
121
|
+
│ │ ├── services/ # Business logic
|
|
122
|
+
│ │ │ ├── storage.ts
|
|
123
|
+
│ │ │ └── updater.ts
|
|
124
|
+
│ │ └── windows/ # Window management
|
|
125
|
+
│ │ ├── main-window.ts
|
|
126
|
+
│ │ └── settings-window.ts
|
|
127
|
+
│ ├── preload/ # Preload scripts
|
|
128
|
+
│ │ ├── index.ts # Main preload
|
|
129
|
+
│ │ └── api.ts # Exposed APIs
|
|
130
|
+
│ ├── renderer/ # React/Vue/Svelte app
|
|
131
|
+
│ │ ├── src/
|
|
132
|
+
│ │ ├── index.html
|
|
133
|
+
│ │ └── vite.config.ts
|
|
134
|
+
│ └── shared/ # Shared types/constants
|
|
135
|
+
│ ├── types.ts
|
|
136
|
+
│ └── constants.ts
|
|
137
|
+
├── resources/ # App resources
|
|
138
|
+
│ ├── icons/
|
|
139
|
+
│ └── locales/
|
|
140
|
+
├── electron.vite.config.ts
|
|
141
|
+
├── electron-builder.yml
|
|
142
|
+
└── package.json
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Main Process APIs
|
|
148
|
+
|
|
149
|
+
### App Lifecycle
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// src/main/app.ts
|
|
153
|
+
import { app, BrowserWindow, session } from "electron";
|
|
154
|
+
import { join } from "path";
|
|
155
|
+
|
|
156
|
+
class Application {
|
|
157
|
+
private mainWindow: BrowserWindow | null = null;
|
|
158
|
+
|
|
159
|
+
async initialize(): Promise<void> {
|
|
160
|
+
// Set app user model ID for Windows
|
|
161
|
+
if (process.platform === "win32") {
|
|
162
|
+
app.setAppUserModelId(app.getName());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Prevent multiple instances
|
|
166
|
+
const gotSingleLock = app.requestSingleInstanceLock();
|
|
167
|
+
if (!gotSingleLock) {
|
|
168
|
+
app.quit();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
app.on("second-instance", () => {
|
|
173
|
+
if (this.mainWindow) {
|
|
174
|
+
if (this.mainWindow.isMinimized()) {
|
|
175
|
+
this.mainWindow.restore();
|
|
176
|
+
}
|
|
177
|
+
this.mainWindow.focus();
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// macOS: Re-create window when dock icon clicked
|
|
182
|
+
app.on("activate", () => {
|
|
183
|
+
if (BrowserWindow.getAllWindows().length === 0) {
|
|
184
|
+
this.createMainWindow();
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Quit when all windows closed (except macOS)
|
|
189
|
+
app.on("window-all-closed", () => {
|
|
190
|
+
if (process.platform !== "darwin") {
|
|
191
|
+
app.quit();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Wait for ready
|
|
196
|
+
await app.whenReady();
|
|
197
|
+
|
|
198
|
+
// Configure session
|
|
199
|
+
this.configureSession();
|
|
200
|
+
|
|
201
|
+
// Create main window
|
|
202
|
+
this.createMainWindow();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private configureSession(): void {
|
|
206
|
+
// Configure Content Security Policy
|
|
207
|
+
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
|
208
|
+
callback({
|
|
209
|
+
responseHeaders: {
|
|
210
|
+
...details.responseHeaders,
|
|
211
|
+
"Content-Security-Policy": [
|
|
212
|
+
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'",
|
|
213
|
+
],
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Configure permissions
|
|
219
|
+
session.defaultSession.setPermissionRequestHandler(
|
|
220
|
+
(webContents, permission, callback) => {
|
|
221
|
+
const allowedPermissions = ["notifications", "clipboard-read"];
|
|
222
|
+
callback(allowedPermissions.includes(permission));
|
|
223
|
+
},
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private createMainWindow(): void {
|
|
228
|
+
this.mainWindow = new BrowserWindow({
|
|
229
|
+
width: 1200,
|
|
230
|
+
height: 800,
|
|
231
|
+
minWidth: 800,
|
|
232
|
+
minHeight: 600,
|
|
233
|
+
show: false,
|
|
234
|
+
webPreferences: {
|
|
235
|
+
preload: join(__dirname, "../preload/index.js"),
|
|
236
|
+
sandbox: true,
|
|
237
|
+
contextIsolation: true,
|
|
238
|
+
nodeIntegration: false,
|
|
239
|
+
webSecurity: true,
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Show window when ready
|
|
244
|
+
this.mainWindow.on("ready-to-show", () => {
|
|
245
|
+
this.mainWindow?.show();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// Load app content
|
|
249
|
+
if (process.env.NODE_ENV === "development") {
|
|
250
|
+
this.mainWindow.loadURL("http://localhost:5173");
|
|
251
|
+
this.mainWindow.webContents.openDevTools();
|
|
252
|
+
} else {
|
|
253
|
+
this.mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export const application = new Application();
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Window Management
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// src/main/windows/window-manager.ts
|
|
265
|
+
import {
|
|
266
|
+
BrowserWindow,
|
|
267
|
+
BrowserWindowConstructorOptions,
|
|
268
|
+
screen,
|
|
269
|
+
} from "electron";
|
|
270
|
+
import { join } from "path";
|
|
271
|
+
|
|
272
|
+
interface WindowState {
|
|
273
|
+
width: number;
|
|
274
|
+
height: number;
|
|
275
|
+
x?: number;
|
|
276
|
+
y?: number;
|
|
277
|
+
isMaximized: boolean;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export class WindowManager {
|
|
281
|
+
private windows = new Map<string, BrowserWindow>();
|
|
282
|
+
private stateStore: Map<string, WindowState> = new Map();
|
|
283
|
+
|
|
284
|
+
createWindow(
|
|
285
|
+
id: string,
|
|
286
|
+
options: BrowserWindowConstructorOptions = {},
|
|
287
|
+
): BrowserWindow {
|
|
288
|
+
// Restore previous state
|
|
289
|
+
const savedState = this.stateStore.get(id);
|
|
290
|
+
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
|
|
291
|
+
|
|
292
|
+
const defaultOptions: BrowserWindowConstructorOptions = {
|
|
293
|
+
width: savedState?.width ?? Math.floor(width * 0.8),
|
|
294
|
+
height: savedState?.height ?? Math.floor(height * 0.8),
|
|
295
|
+
x: savedState?.x,
|
|
296
|
+
y: savedState?.y,
|
|
297
|
+
show: false,
|
|
298
|
+
webPreferences: {
|
|
299
|
+
preload: join(__dirname, "../preload/index.js"),
|
|
300
|
+
sandbox: true,
|
|
301
|
+
contextIsolation: true,
|
|
302
|
+
nodeIntegration: false,
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const window = new BrowserWindow({
|
|
307
|
+
...defaultOptions,
|
|
308
|
+
...options,
|
|
309
|
+
webPreferences: {
|
|
310
|
+
...defaultOptions.webPreferences,
|
|
311
|
+
...options.webPreferences,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Restore maximized state
|
|
316
|
+
if (savedState?.isMaximized) {
|
|
317
|
+
window.maximize();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Save state on close
|
|
321
|
+
window.on("close", () => {
|
|
322
|
+
this.saveWindowState(id, window);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
window.on("closed", () => {
|
|
326
|
+
this.windows.delete(id);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
this.windows.set(id, window);
|
|
330
|
+
return window;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
getWindow(id: string): BrowserWindow | undefined {
|
|
334
|
+
return this.windows.get(id);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
closeWindow(id: string): void {
|
|
338
|
+
const window = this.windows.get(id);
|
|
339
|
+
if (window && !window.isDestroyed()) {
|
|
340
|
+
window.close();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
private saveWindowState(id: string, window: BrowserWindow): void {
|
|
345
|
+
const bounds = window.getBounds();
|
|
346
|
+
this.stateStore.set(id, {
|
|
347
|
+
width: bounds.width,
|
|
348
|
+
height: bounds.height,
|
|
349
|
+
x: bounds.x,
|
|
350
|
+
y: bounds.y,
|
|
351
|
+
isMaximized: window.isMaximized(),
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export const windowManager = new WindowManager();
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## IPC Communication
|
|
362
|
+
|
|
363
|
+
### Type-Safe IPC Pattern
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// src/shared/ipc-types.ts
|
|
367
|
+
export interface IpcChannels {
|
|
368
|
+
// Main -> Renderer
|
|
369
|
+
"app:update-available": { version: string };
|
|
370
|
+
"app:update-downloaded": void;
|
|
371
|
+
|
|
372
|
+
// Renderer -> Main (invoke)
|
|
373
|
+
"file:open": { path: string };
|
|
374
|
+
"file:save": { path: string; content: string };
|
|
375
|
+
"file:read": string; // Returns file content
|
|
376
|
+
"window:minimize": void;
|
|
377
|
+
"window:maximize": void;
|
|
378
|
+
"window:close": void;
|
|
379
|
+
"storage:get": { key: string };
|
|
380
|
+
"storage:set": { key: string; value: unknown };
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export type IpcChannel = keyof IpcChannels;
|
|
384
|
+
export type IpcPayload<C extends IpcChannel> = IpcChannels[C];
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Main Process Handlers
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
// src/main/ipc/index.ts
|
|
391
|
+
import { ipcMain, dialog, BrowserWindow } from "electron";
|
|
392
|
+
import { readFile, writeFile } from "fs/promises";
|
|
393
|
+
import Store from "electron-store";
|
|
394
|
+
|
|
395
|
+
const store = new Store();
|
|
396
|
+
|
|
397
|
+
export function registerIpcHandlers(): void {
|
|
398
|
+
// File operations
|
|
399
|
+
ipcMain.handle("file:open", async () => {
|
|
400
|
+
const result = await dialog.showOpenDialog({
|
|
401
|
+
properties: ["openFile"],
|
|
402
|
+
filters: [
|
|
403
|
+
{ name: "All Files", extensions: ["*"] },
|
|
404
|
+
{ name: "Text", extensions: ["txt", "md"] },
|
|
405
|
+
],
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
if (result.canceled || result.filePaths.length === 0) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const filePath = result.filePaths[0];
|
|
413
|
+
const content = await readFile(filePath, "utf-8");
|
|
414
|
+
return { path: filePath, content };
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
ipcMain.handle(
|
|
418
|
+
"file:save",
|
|
419
|
+
async (_event, { path, content }: { path: string; content: string }) => {
|
|
420
|
+
await writeFile(path, content, "utf-8");
|
|
421
|
+
return { success: true };
|
|
422
|
+
},
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
ipcMain.handle("file:read", async (_event, path: string) => {
|
|
426
|
+
return readFile(path, "utf-8");
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
// Window operations
|
|
430
|
+
ipcMain.handle("window:minimize", (event) => {
|
|
431
|
+
const window = BrowserWindow.fromWebContents(event.sender);
|
|
432
|
+
window?.minimize();
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
ipcMain.handle("window:maximize", (event) => {
|
|
436
|
+
const window = BrowserWindow.fromWebContents(event.sender);
|
|
437
|
+
if (window?.isMaximized()) {
|
|
438
|
+
window.unmaximize();
|
|
439
|
+
} else {
|
|
440
|
+
window?.maximize();
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
ipcMain.handle("window:close", (event) => {
|
|
445
|
+
const window = BrowserWindow.fromWebContents(event.sender);
|
|
446
|
+
window?.close();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// Storage operations
|
|
450
|
+
ipcMain.handle("storage:get", (_event, { key }: { key: string }) => {
|
|
451
|
+
return store.get(key);
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
ipcMain.handle(
|
|
455
|
+
"storage:set",
|
|
456
|
+
(_event, { key, value }: { key: string; value: unknown }) => {
|
|
457
|
+
store.set(key, value);
|
|
458
|
+
return { success: true };
|
|
459
|
+
},
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Preload Script
|
|
465
|
+
|
|
466
|
+
```typescript
|
|
467
|
+
// src/preload/index.ts
|
|
468
|
+
import { contextBridge, ipcRenderer } from "electron";
|
|
469
|
+
|
|
470
|
+
// Expose protected methods for renderer
|
|
471
|
+
const api = {
|
|
472
|
+
// Window controls
|
|
473
|
+
window: {
|
|
474
|
+
minimize: () => ipcRenderer.invoke("window:minimize"),
|
|
475
|
+
maximize: () => ipcRenderer.invoke("window:maximize"),
|
|
476
|
+
close: () => ipcRenderer.invoke("window:close"),
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
// File operations
|
|
480
|
+
file: {
|
|
481
|
+
open: () => ipcRenderer.invoke("file:open"),
|
|
482
|
+
save: (path: string, content: string) =>
|
|
483
|
+
ipcRenderer.invoke("file:save", { path, content }),
|
|
484
|
+
read: (path: string) => ipcRenderer.invoke("file:read", path),
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
// Storage
|
|
488
|
+
storage: {
|
|
489
|
+
get: <T>(key: string): Promise<T | undefined> =>
|
|
490
|
+
ipcRenderer.invoke("storage:get", { key }),
|
|
491
|
+
set: (key: string, value: unknown) =>
|
|
492
|
+
ipcRenderer.invoke("storage:set", { key, value }),
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
// App events
|
|
496
|
+
onUpdateAvailable: (callback: (version: string) => void) => {
|
|
497
|
+
const handler = (
|
|
498
|
+
_event: Electron.IpcRendererEvent,
|
|
499
|
+
{ version }: { version: string },
|
|
500
|
+
) => {
|
|
501
|
+
callback(version);
|
|
502
|
+
};
|
|
503
|
+
ipcRenderer.on("app:update-available", handler);
|
|
504
|
+
return () => ipcRenderer.removeListener("app:update-available", handler);
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
onUpdateDownloaded: (callback: () => void) => {
|
|
508
|
+
const handler = () => callback();
|
|
509
|
+
ipcRenderer.on("app:update-downloaded", handler);
|
|
510
|
+
return () => ipcRenderer.removeListener("app:update-downloaded", handler);
|
|
511
|
+
},
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
contextBridge.exposeInMainWorld("electronAPI", api);
|
|
515
|
+
|
|
516
|
+
// Type declaration for renderer
|
|
517
|
+
declare global {
|
|
518
|
+
interface Window {
|
|
519
|
+
electronAPI: typeof api;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Auto-Update
|
|
527
|
+
|
|
528
|
+
### Update Service
|
|
529
|
+
|
|
530
|
+
```typescript
|
|
531
|
+
// src/main/services/updater.ts
|
|
532
|
+
import { autoUpdater, UpdateInfo } from "electron-updater";
|
|
533
|
+
import { BrowserWindow, dialog } from "electron";
|
|
534
|
+
import log from "electron-log";
|
|
535
|
+
|
|
536
|
+
export class UpdateService {
|
|
537
|
+
private mainWindow: BrowserWindow | null = null;
|
|
538
|
+
|
|
539
|
+
initialize(window: BrowserWindow): void {
|
|
540
|
+
this.mainWindow = window;
|
|
541
|
+
|
|
542
|
+
// Configure logging
|
|
543
|
+
autoUpdater.logger = log;
|
|
544
|
+
autoUpdater.autoDownload = false;
|
|
545
|
+
autoUpdater.autoInstallOnAppQuit = true;
|
|
546
|
+
|
|
547
|
+
// Event handlers
|
|
548
|
+
autoUpdater.on("checking-for-update", () => {
|
|
549
|
+
log.info("Checking for updates...");
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
autoUpdater.on("update-available", (info: UpdateInfo) => {
|
|
553
|
+
log.info("Update available:", info.version);
|
|
554
|
+
this.mainWindow?.webContents.send("app:update-available", {
|
|
555
|
+
version: info.version,
|
|
556
|
+
});
|
|
557
|
+
this.promptForUpdate(info);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
autoUpdater.on("update-not-available", () => {
|
|
561
|
+
log.info("No updates available");
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
autoUpdater.on("error", (error) => {
|
|
565
|
+
log.error("Update error:", error);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
autoUpdater.on("download-progress", (progress) => {
|
|
569
|
+
log.info(`Download progress: ${progress.percent.toFixed(1)}%`);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
autoUpdater.on("update-downloaded", () => {
|
|
573
|
+
log.info("Update downloaded");
|
|
574
|
+
this.mainWindow?.webContents.send("app:update-downloaded");
|
|
575
|
+
this.promptForRestart();
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
async checkForUpdates(): Promise<void> {
|
|
580
|
+
try {
|
|
581
|
+
await autoUpdater.checkForUpdates();
|
|
582
|
+
} catch (error) {
|
|
583
|
+
log.error("Failed to check for updates:", error);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
private async promptForUpdate(info: UpdateInfo): Promise<void> {
|
|
588
|
+
const result = await dialog.showMessageBox(this.mainWindow!, {
|
|
589
|
+
type: "info",
|
|
590
|
+
title: "Update Available",
|
|
591
|
+
message: `Version ${info.version} is available. Would you like to download it?`,
|
|
592
|
+
buttons: ["Download", "Later"],
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
if (result.response === 0) {
|
|
596
|
+
autoUpdater.downloadUpdate();
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
private async promptForRestart(): Promise<void> {
|
|
601
|
+
const result = await dialog.showMessageBox(this.mainWindow!, {
|
|
602
|
+
type: "info",
|
|
603
|
+
title: "Update Ready",
|
|
604
|
+
message:
|
|
605
|
+
"A new version has been downloaded. Restart to apply the update?",
|
|
606
|
+
buttons: ["Restart Now", "Later"],
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
if (result.response === 0) {
|
|
610
|
+
autoUpdater.quitAndInstall(false, true);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export const updateService = new UpdateService();
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
---
|
|
619
|
+
|
|
620
|
+
## Security Best Practices
|
|
621
|
+
|
|
622
|
+
### Security Checklist
|
|
623
|
+
|
|
624
|
+
Mandatory Security Settings:
|
|
625
|
+
|
|
626
|
+
- contextIsolation: true (always enable)
|
|
627
|
+
- nodeIntegration: false (never enable in renderer)
|
|
628
|
+
- sandbox: true (always enable)
|
|
629
|
+
- webSecurity: true (never disable)
|
|
630
|
+
|
|
631
|
+
IPC Security Rules:
|
|
632
|
+
|
|
633
|
+
- Validate all inputs from renderer
|
|
634
|
+
- Never expose Node.js APIs directly
|
|
635
|
+
- Use invoke/handle pattern (not send/on for sensitive operations)
|
|
636
|
+
- Whitelist allowed operations
|
|
637
|
+
|
|
638
|
+
Content Security Policy:
|
|
639
|
+
|
|
640
|
+
- Restrict script sources to 'self'
|
|
641
|
+
- Disable unsafe-inline for scripts
|
|
642
|
+
- Use nonce or hash for inline scripts if needed
|
|
643
|
+
|
|
644
|
+
### Input Validation
|
|
645
|
+
|
|
646
|
+
```typescript
|
|
647
|
+
// src/main/ipc/validators.ts
|
|
648
|
+
import { z } from "zod";
|
|
649
|
+
|
|
650
|
+
const FilePathSchema = z.string().refine(
|
|
651
|
+
(path) => {
|
|
652
|
+
// Prevent path traversal
|
|
653
|
+
const normalized = path.replace(/\\/g, "/");
|
|
654
|
+
return !normalized.includes("..") && !normalized.startsWith("/");
|
|
655
|
+
},
|
|
656
|
+
{ message: "Invalid file path" },
|
|
657
|
+
);
|
|
658
|
+
|
|
659
|
+
const StorageKeySchema = z
|
|
660
|
+
.string()
|
|
661
|
+
.min(1)
|
|
662
|
+
.max(100)
|
|
663
|
+
.regex(/^[a-zA-Z0-9_.-]+$/);
|
|
664
|
+
|
|
665
|
+
export const validators = {
|
|
666
|
+
filePath: (path: unknown) => FilePathSchema.parse(path),
|
|
667
|
+
storageKey: (key: unknown) => StorageKeySchema.parse(key),
|
|
668
|
+
};
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
---
|
|
672
|
+
|
|
673
|
+
## Native Integration
|
|
674
|
+
|
|
675
|
+
### System Tray
|
|
676
|
+
|
|
677
|
+
```typescript
|
|
678
|
+
// src/main/services/tray.ts
|
|
679
|
+
import { Tray, Menu, app, nativeImage } from "electron";
|
|
680
|
+
import { join } from "path";
|
|
681
|
+
|
|
682
|
+
export class TrayService {
|
|
683
|
+
private tray: Tray | null = null;
|
|
684
|
+
|
|
685
|
+
initialize(): void {
|
|
686
|
+
const iconPath = join(__dirname, "../../resources/icons/tray.png");
|
|
687
|
+
const icon = nativeImage.createFromPath(iconPath);
|
|
688
|
+
|
|
689
|
+
this.tray = new Tray(icon);
|
|
690
|
+
this.tray.setToolTip(app.getName());
|
|
691
|
+
|
|
692
|
+
const contextMenu = Menu.buildFromTemplate([
|
|
693
|
+
{
|
|
694
|
+
label: "Show App",
|
|
695
|
+
click: () => {
|
|
696
|
+
const { windowManager } = require("./window-manager");
|
|
697
|
+
const mainWindow = windowManager.getWindow("main");
|
|
698
|
+
mainWindow?.show();
|
|
699
|
+
mainWindow?.focus();
|
|
700
|
+
},
|
|
701
|
+
},
|
|
702
|
+
{ type: "separator" },
|
|
703
|
+
{
|
|
704
|
+
label: "Preferences",
|
|
705
|
+
accelerator: "CmdOrCtrl+,",
|
|
706
|
+
click: () => {
|
|
707
|
+
// Open preferences
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
{ type: "separator" },
|
|
711
|
+
{
|
|
712
|
+
label: "Quit",
|
|
713
|
+
accelerator: "CmdOrCtrl+Q",
|
|
714
|
+
click: () => app.quit(),
|
|
715
|
+
},
|
|
716
|
+
]);
|
|
717
|
+
|
|
718
|
+
this.tray.setContextMenu(contextMenu);
|
|
719
|
+
|
|
720
|
+
// macOS: Click to show app
|
|
721
|
+
this.tray.on("click", () => {
|
|
722
|
+
const { windowManager } = require("./window-manager");
|
|
723
|
+
const mainWindow = windowManager.getWindow("main");
|
|
724
|
+
mainWindow?.show();
|
|
725
|
+
mainWindow?.focus();
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
destroy(): void {
|
|
730
|
+
this.tray?.destroy();
|
|
731
|
+
this.tray = null;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export const trayService = new TrayService();
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### Native Menu
|
|
739
|
+
|
|
740
|
+
```typescript
|
|
741
|
+
// src/main/services/menu.ts
|
|
742
|
+
import { Menu, app, shell, MenuItemConstructorOptions } from "electron";
|
|
743
|
+
|
|
744
|
+
export function createApplicationMenu(): void {
|
|
745
|
+
const isMac = process.platform === "darwin";
|
|
746
|
+
|
|
747
|
+
const template: MenuItemConstructorOptions[] = [
|
|
748
|
+
// App menu (macOS only)
|
|
749
|
+
...(isMac
|
|
750
|
+
? [
|
|
751
|
+
{
|
|
752
|
+
label: app.name,
|
|
753
|
+
submenu: [
|
|
754
|
+
{ role: "about" as const },
|
|
755
|
+
{ type: "separator" as const },
|
|
756
|
+
{ role: "services" as const },
|
|
757
|
+
{ type: "separator" as const },
|
|
758
|
+
{ role: "hide" as const },
|
|
759
|
+
{ role: "hideOthers" as const },
|
|
760
|
+
{ role: "unhide" as const },
|
|
761
|
+
{ type: "separator" as const },
|
|
762
|
+
{ role: "quit" as const },
|
|
763
|
+
],
|
|
764
|
+
},
|
|
765
|
+
]
|
|
766
|
+
: []),
|
|
767
|
+
|
|
768
|
+
// File menu
|
|
769
|
+
{
|
|
770
|
+
label: "File",
|
|
771
|
+
submenu: [
|
|
772
|
+
{
|
|
773
|
+
label: "New",
|
|
774
|
+
accelerator: "CmdOrCtrl+N",
|
|
775
|
+
click: () => {
|
|
776
|
+
// Handle new file
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
label: "Open...",
|
|
781
|
+
accelerator: "CmdOrCtrl+O",
|
|
782
|
+
click: () => {
|
|
783
|
+
// Handle open
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
{ type: "separator" },
|
|
787
|
+
{
|
|
788
|
+
label: "Save",
|
|
789
|
+
accelerator: "CmdOrCtrl+S",
|
|
790
|
+
click: () => {
|
|
791
|
+
// Handle save
|
|
792
|
+
},
|
|
793
|
+
},
|
|
794
|
+
{ type: "separator" },
|
|
795
|
+
isMac ? { role: "close" } : { role: "quit" },
|
|
796
|
+
],
|
|
797
|
+
},
|
|
798
|
+
|
|
799
|
+
// Edit menu
|
|
800
|
+
{
|
|
801
|
+
label: "Edit",
|
|
802
|
+
submenu: [
|
|
803
|
+
{ role: "undo" },
|
|
804
|
+
{ role: "redo" },
|
|
805
|
+
{ type: "separator" },
|
|
806
|
+
{ role: "cut" },
|
|
807
|
+
{ role: "copy" },
|
|
808
|
+
{ role: "paste" },
|
|
809
|
+
{ role: "selectAll" },
|
|
810
|
+
],
|
|
811
|
+
},
|
|
812
|
+
|
|
813
|
+
// View menu
|
|
814
|
+
{
|
|
815
|
+
label: "View",
|
|
816
|
+
submenu: [
|
|
817
|
+
{ role: "reload" },
|
|
818
|
+
{ role: "forceReload" },
|
|
819
|
+
{ role: "toggleDevTools" },
|
|
820
|
+
{ type: "separator" },
|
|
821
|
+
{ role: "resetZoom" },
|
|
822
|
+
{ role: "zoomIn" },
|
|
823
|
+
{ role: "zoomOut" },
|
|
824
|
+
{ type: "separator" },
|
|
825
|
+
{ role: "togglefullscreen" },
|
|
826
|
+
],
|
|
827
|
+
},
|
|
828
|
+
|
|
829
|
+
// Help menu
|
|
830
|
+
{
|
|
831
|
+
label: "Help",
|
|
832
|
+
submenu: [
|
|
833
|
+
{
|
|
834
|
+
label: "Documentation",
|
|
835
|
+
click: () => shell.openExternal("https://docs.example.com"),
|
|
836
|
+
},
|
|
837
|
+
{
|
|
838
|
+
label: "Report Issue",
|
|
839
|
+
click: () =>
|
|
840
|
+
shell.openExternal("https://github.com/example/repo/issues"),
|
|
841
|
+
},
|
|
842
|
+
],
|
|
843
|
+
},
|
|
844
|
+
];
|
|
845
|
+
|
|
846
|
+
const menu = Menu.buildFromTemplate(template);
|
|
847
|
+
Menu.setApplicationMenu(menu);
|
|
848
|
+
}
|
|
849
|
+
```
|
|
850
|
+
|
|
851
|
+
---
|
|
852
|
+
|
|
853
|
+
## Configuration
|
|
854
|
+
|
|
855
|
+
### Electron Forge Configuration
|
|
856
|
+
|
|
857
|
+
```javascript
|
|
858
|
+
// forge.config.js
|
|
859
|
+
module.exports = {
|
|
860
|
+
packagerConfig: {
|
|
861
|
+
asar: true,
|
|
862
|
+
darwinDarkModeSupport: true,
|
|
863
|
+
executableName: "my-app",
|
|
864
|
+
appBundleId: "com.example.myapp",
|
|
865
|
+
appCategoryType: "public.app-category.developer-tools",
|
|
866
|
+
icon: "./resources/icons/icon",
|
|
867
|
+
osxSign: {
|
|
868
|
+
identity: "Developer ID Application: Your Name (TEAM_ID)",
|
|
869
|
+
"hardened-runtime": true,
|
|
870
|
+
entitlements: "./entitlements.plist",
|
|
871
|
+
"entitlements-inherit": "./entitlements.plist",
|
|
872
|
+
"signature-flags": "library",
|
|
873
|
+
},
|
|
874
|
+
osxNotarize: {
|
|
875
|
+
appleId: process.env.APPLE_ID,
|
|
876
|
+
appleIdPassword: process.env.APPLE_PASSWORD,
|
|
877
|
+
teamId: process.env.APPLE_TEAM_ID,
|
|
878
|
+
},
|
|
879
|
+
},
|
|
880
|
+
rebuildConfig: {},
|
|
881
|
+
makers: [
|
|
882
|
+
{
|
|
883
|
+
name: "@electron-forge/maker-squirrel",
|
|
884
|
+
config: {
|
|
885
|
+
name: "my_app",
|
|
886
|
+
setupIcon: "./resources/icons/icon.ico",
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
name: "@electron-forge/maker-zip",
|
|
891
|
+
platforms: ["darwin"],
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
name: "@electron-forge/maker-dmg",
|
|
895
|
+
config: {
|
|
896
|
+
icon: "./resources/icons/icon.icns",
|
|
897
|
+
format: "ULFO",
|
|
898
|
+
},
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
name: "@electron-forge/maker-deb",
|
|
902
|
+
config: {
|
|
903
|
+
options: {
|
|
904
|
+
maintainer: "Your Name",
|
|
905
|
+
homepage: "https://example.com",
|
|
906
|
+
},
|
|
907
|
+
},
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
name: "@electron-forge/maker-rpm",
|
|
911
|
+
config: {},
|
|
912
|
+
},
|
|
913
|
+
],
|
|
914
|
+
plugins: [
|
|
915
|
+
{
|
|
916
|
+
name: "@electron-forge/plugin-vite",
|
|
917
|
+
config: {
|
|
918
|
+
build: [
|
|
919
|
+
{
|
|
920
|
+
entry: "src/main/index.ts",
|
|
921
|
+
config: "vite.main.config.ts",
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
entry: "src/preload/index.ts",
|
|
925
|
+
config: "vite.preload.config.ts",
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
renderer: [
|
|
929
|
+
{
|
|
930
|
+
name: "main_window",
|
|
931
|
+
config: "vite.renderer.config.ts",
|
|
932
|
+
},
|
|
933
|
+
],
|
|
934
|
+
},
|
|
935
|
+
},
|
|
936
|
+
],
|
|
937
|
+
publishers: [
|
|
938
|
+
{
|
|
939
|
+
name: "@electron-forge/publisher-github",
|
|
940
|
+
config: {
|
|
941
|
+
repository: {
|
|
942
|
+
owner: "your-username",
|
|
943
|
+
name: "your-repo",
|
|
944
|
+
},
|
|
945
|
+
prerelease: false,
|
|
946
|
+
},
|
|
947
|
+
},
|
|
948
|
+
],
|
|
949
|
+
};
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
### Electron Builder Configuration
|
|
953
|
+
|
|
954
|
+
```yaml
|
|
955
|
+
# electron-builder.yml
|
|
956
|
+
appId: com.example.myapp
|
|
957
|
+
productName: My App
|
|
958
|
+
copyright: Copyright (c) 2025 Your Name
|
|
959
|
+
|
|
960
|
+
directories:
|
|
961
|
+
output: dist
|
|
962
|
+
buildResources: resources
|
|
963
|
+
|
|
964
|
+
files:
|
|
965
|
+
- "!**/.vscode/*"
|
|
966
|
+
- "!src/*"
|
|
967
|
+
- "!docs/*"
|
|
968
|
+
- "!*.md"
|
|
969
|
+
|
|
970
|
+
extraResources:
|
|
971
|
+
- from: resources/
|
|
972
|
+
to: resources/
|
|
973
|
+
filter:
|
|
974
|
+
- "**/*"
|
|
975
|
+
|
|
976
|
+
asar: true
|
|
977
|
+
compression: maximum
|
|
978
|
+
|
|
979
|
+
mac:
|
|
980
|
+
category: public.app-category.developer-tools
|
|
981
|
+
icon: resources/icons/icon.icns
|
|
982
|
+
hardenedRuntime: true
|
|
983
|
+
gatekeeperAssess: false
|
|
984
|
+
entitlements: entitlements.mac.plist
|
|
985
|
+
entitlementsInherit: entitlements.mac.plist
|
|
986
|
+
notarize:
|
|
987
|
+
teamId: ${APPLE_TEAM_ID}
|
|
988
|
+
target:
|
|
989
|
+
- target: dmg
|
|
990
|
+
arch: [x64, arm64]
|
|
991
|
+
- target: zip
|
|
992
|
+
arch: [x64, arm64]
|
|
993
|
+
|
|
994
|
+
dmg:
|
|
995
|
+
sign: false
|
|
996
|
+
contents:
|
|
997
|
+
- x: 130
|
|
998
|
+
y: 220
|
|
999
|
+
- x: 410
|
|
1000
|
+
y: 220
|
|
1001
|
+
type: link
|
|
1002
|
+
path: /Applications
|
|
1003
|
+
|
|
1004
|
+
win:
|
|
1005
|
+
icon: resources/icons/icon.ico
|
|
1006
|
+
signingHashAlgorithms: [sha256]
|
|
1007
|
+
signAndEditExecutable: true
|
|
1008
|
+
target:
|
|
1009
|
+
- target: nsis
|
|
1010
|
+
arch: [x64]
|
|
1011
|
+
- target: portable
|
|
1012
|
+
arch: [x64]
|
|
1013
|
+
|
|
1014
|
+
nsis:
|
|
1015
|
+
oneClick: false
|
|
1016
|
+
allowToChangeInstallationDirectory: true
|
|
1017
|
+
installerIcon: resources/icons/icon.ico
|
|
1018
|
+
uninstallerIcon: resources/icons/icon.ico
|
|
1019
|
+
installerHeaderIcon: resources/icons/icon.ico
|
|
1020
|
+
createDesktopShortcut: true
|
|
1021
|
+
createStartMenuShortcut: true
|
|
1022
|
+
|
|
1023
|
+
linux:
|
|
1024
|
+
icon: resources/icons
|
|
1025
|
+
category: Development
|
|
1026
|
+
target:
|
|
1027
|
+
- target: AppImage
|
|
1028
|
+
arch: [x64]
|
|
1029
|
+
- target: deb
|
|
1030
|
+
arch: [x64]
|
|
1031
|
+
- target: rpm
|
|
1032
|
+
arch: [x64]
|
|
1033
|
+
|
|
1034
|
+
publish:
|
|
1035
|
+
provider: github
|
|
1036
|
+
owner: your-username
|
|
1037
|
+
repo: your-repo
|
|
1038
|
+
```
|
|
1039
|
+
|
|
1040
|
+
---
|
|
1041
|
+
|
|
1042
|
+
## Utility Process (Electron 33+)
|
|
1043
|
+
|
|
1044
|
+
### Background Task Worker
|
|
1045
|
+
|
|
1046
|
+
Utility processes run in a separate Node.js environment for CPU-intensive tasks without blocking the main process:
|
|
1047
|
+
|
|
1048
|
+
```typescript
|
|
1049
|
+
// src/main/workers/utility-worker.ts
|
|
1050
|
+
import { utilityProcess, MessageChannelMain } from "electron";
|
|
1051
|
+
import { join } from "path";
|
|
1052
|
+
|
|
1053
|
+
export class UtilityWorker {
|
|
1054
|
+
private worker: Electron.UtilityProcess | null = null;
|
|
1055
|
+
private port: Electron.MessagePortMain | null = null;
|
|
1056
|
+
|
|
1057
|
+
async spawn(): Promise<void> {
|
|
1058
|
+
const { port1, port2 } = new MessageChannelMain();
|
|
1059
|
+
|
|
1060
|
+
this.worker = utilityProcess.fork(
|
|
1061
|
+
join(__dirname, "workers/image-processor.js"),
|
|
1062
|
+
[],
|
|
1063
|
+
{
|
|
1064
|
+
serviceName: "image-processor",
|
|
1065
|
+
allowLoadingUnsignedLibraries: false,
|
|
1066
|
+
}
|
|
1067
|
+
);
|
|
1068
|
+
|
|
1069
|
+
this.worker.postMessage({ type: "init" }, [port1]);
|
|
1070
|
+
this.port = port2;
|
|
1071
|
+
|
|
1072
|
+
this.worker.on("exit", (code) => {
|
|
1073
|
+
console.log(`Utility process exited with code ${code}`);
|
|
1074
|
+
this.worker = null;
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
async processImage(imagePath: string): Promise<Buffer> {
|
|
1079
|
+
return new Promise((resolve, reject) => {
|
|
1080
|
+
if (!this.port) {
|
|
1081
|
+
reject(new Error("Worker not initialized"));
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
const handler = (event: Electron.MessageEvent) => {
|
|
1086
|
+
if (event.data.type === "result") {
|
|
1087
|
+
this.port?.removeListener("message", handler);
|
|
1088
|
+
resolve(Buffer.from(event.data.buffer));
|
|
1089
|
+
} else if (event.data.type === "error") {
|
|
1090
|
+
this.port?.removeListener("message", handler);
|
|
1091
|
+
reject(new Error(event.data.message));
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
this.port.on("message", handler);
|
|
1096
|
+
this.port.postMessage({ type: "process", path: imagePath });
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
terminate(): void {
|
|
1101
|
+
this.worker?.kill();
|
|
1102
|
+
this.worker = null;
|
|
1103
|
+
this.port = null;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
### Utility Process Script
|
|
1109
|
+
|
|
1110
|
+
```typescript
|
|
1111
|
+
// src/main/workers/image-processor.js
|
|
1112
|
+
const sharp = require("sharp");
|
|
1113
|
+
|
|
1114
|
+
process.parentPort.on("message", async (event) => {
|
|
1115
|
+
const [port] = event.ports;
|
|
1116
|
+
|
|
1117
|
+
port.on("message", async (msgEvent) => {
|
|
1118
|
+
const { type, path } = msgEvent.data;
|
|
1119
|
+
|
|
1120
|
+
if (type === "process") {
|
|
1121
|
+
try {
|
|
1122
|
+
const buffer = await sharp(path)
|
|
1123
|
+
.resize(800, 600, { fit: "inside" })
|
|
1124
|
+
.webp({ quality: 80 })
|
|
1125
|
+
.toBuffer();
|
|
1126
|
+
|
|
1127
|
+
port.postMessage({ type: "result", buffer });
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
port.postMessage({ type: "error", message: error.message });
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
|
|
1134
|
+
port.start();
|
|
1135
|
+
});
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
---
|
|
1139
|
+
|
|
1140
|
+
## Protocol Handlers and Deep Linking
|
|
1141
|
+
|
|
1142
|
+
### Custom Protocol Registration
|
|
1143
|
+
|
|
1144
|
+
```typescript
|
|
1145
|
+
// src/main/protocol.ts
|
|
1146
|
+
import { app, protocol, net } from "electron";
|
|
1147
|
+
import { join } from "path";
|
|
1148
|
+
import { pathToFileURL } from "url";
|
|
1149
|
+
|
|
1150
|
+
const PROTOCOL_NAME = "myapp";
|
|
1151
|
+
|
|
1152
|
+
export function registerProtocols(): void {
|
|
1153
|
+
// Register as default protocol client (for deep linking)
|
|
1154
|
+
if (process.defaultApp) {
|
|
1155
|
+
if (process.argv.length >= 2) {
|
|
1156
|
+
app.setAsDefaultProtocolClient(PROTOCOL_NAME, process.execPath, [
|
|
1157
|
+
join(process.argv[1]),
|
|
1158
|
+
]);
|
|
1159
|
+
}
|
|
1160
|
+
} else {
|
|
1161
|
+
app.setAsDefaultProtocolClient(PROTOCOL_NAME);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
// Register custom protocol handler for local resources
|
|
1165
|
+
protocol.handle("app", (request) => {
|
|
1166
|
+
const url = new URL(request.url);
|
|
1167
|
+
const filePath = join(__dirname, "../renderer", url.pathname);
|
|
1168
|
+
return net.fetch(pathToFileURL(filePath).toString());
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
export function handleProtocolUrl(url: string): void {
|
|
1173
|
+
// Parse the URL: myapp://action/path?query=value
|
|
1174
|
+
const parsedUrl = new URL(url);
|
|
1175
|
+
|
|
1176
|
+
switch (parsedUrl.hostname) {
|
|
1177
|
+
case "open":
|
|
1178
|
+
handleOpenAction(parsedUrl.pathname, parsedUrl.searchParams);
|
|
1179
|
+
break;
|
|
1180
|
+
case "auth":
|
|
1181
|
+
handleAuthCallback(parsedUrl.searchParams);
|
|
1182
|
+
break;
|
|
1183
|
+
default:
|
|
1184
|
+
console.warn("Unknown protocol action:", parsedUrl.hostname);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
function handleOpenAction(
|
|
1189
|
+
path: string,
|
|
1190
|
+
params: URLSearchParams
|
|
1191
|
+
): void {
|
|
1192
|
+
// Handle open file/project action
|
|
1193
|
+
const filePath = decodeURIComponent(path.slice(1));
|
|
1194
|
+
// Send to renderer or process directly
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function handleAuthCallback(params: URLSearchParams): void {
|
|
1198
|
+
// Handle OAuth callback
|
|
1199
|
+
const code = params.get("code");
|
|
1200
|
+
const state = params.get("state");
|
|
1201
|
+
// Process authentication
|
|
1202
|
+
}
|
|
1203
|
+
```
|
|
1204
|
+
|
|
1205
|
+
### macOS Deep Link Handling
|
|
1206
|
+
|
|
1207
|
+
```typescript
|
|
1208
|
+
// src/main/index.ts
|
|
1209
|
+
app.on("open-url", (event, url) => {
|
|
1210
|
+
event.preventDefault();
|
|
1211
|
+
handleProtocolUrl(url);
|
|
1212
|
+
});
|
|
1213
|
+
|
|
1214
|
+
// Handle deep link on Windows/Linux (second instance)
|
|
1215
|
+
app.on("second-instance", (_event, commandLine) => {
|
|
1216
|
+
const url = commandLine.find((arg) => arg.startsWith(`${PROTOCOL_NAME}://`));
|
|
1217
|
+
if (url) {
|
|
1218
|
+
handleProtocolUrl(url);
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// Focus the main window
|
|
1222
|
+
const mainWindow = windowManager.getWindow("main");
|
|
1223
|
+
if (mainWindow) {
|
|
1224
|
+
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
1225
|
+
mainWindow.focus();
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
## Security Hardening (OWASP Aligned)
|
|
1233
|
+
|
|
1234
|
+
### Comprehensive Security Configuration
|
|
1235
|
+
|
|
1236
|
+
```typescript
|
|
1237
|
+
// src/main/security.ts
|
|
1238
|
+
import { app, session, shell, BrowserWindow } from "electron";
|
|
1239
|
+
|
|
1240
|
+
export function configureSecurity(): void {
|
|
1241
|
+
// 1. Disable remote module (deprecated but ensure disabled)
|
|
1242
|
+
app.on("remote-get-builtin", (event) => event.preventDefault());
|
|
1243
|
+
app.on("remote-get-current-web-contents", (event) => event.preventDefault());
|
|
1244
|
+
app.on("remote-get-current-window", (event) => event.preventDefault());
|
|
1245
|
+
|
|
1246
|
+
// 2. Block navigation to untrusted origins
|
|
1247
|
+
app.on("web-contents-created", (_event, contents) => {
|
|
1248
|
+
// Block navigation
|
|
1249
|
+
contents.on("will-navigate", (event, url) => {
|
|
1250
|
+
const allowedOrigins = ["http://localhost", "file://"];
|
|
1251
|
+
const isAllowed = allowedOrigins.some((origin) => url.startsWith(origin));
|
|
1252
|
+
if (!isAllowed) {
|
|
1253
|
+
event.preventDefault();
|
|
1254
|
+
console.warn("Blocked navigation to:", url);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
// Block new windows
|
|
1259
|
+
contents.setWindowOpenHandler(({ url }) => {
|
|
1260
|
+
// Open external URLs in default browser
|
|
1261
|
+
if (url.startsWith("https://") || url.startsWith("http://")) {
|
|
1262
|
+
shell.openExternal(url);
|
|
1263
|
+
}
|
|
1264
|
+
return { action: "deny" };
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1267
|
+
// Block webview creation
|
|
1268
|
+
contents.on("will-attach-webview", (event) => {
|
|
1269
|
+
event.preventDefault();
|
|
1270
|
+
console.warn("Blocked webview creation");
|
|
1271
|
+
});
|
|
1272
|
+
});
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
export function configureSessionSecurity(): void {
|
|
1276
|
+
const ses = session.defaultSession;
|
|
1277
|
+
|
|
1278
|
+
// Content Security Policy
|
|
1279
|
+
ses.webRequest.onHeadersReceived((details, callback) => {
|
|
1280
|
+
callback({
|
|
1281
|
+
responseHeaders: {
|
|
1282
|
+
...details.responseHeaders,
|
|
1283
|
+
"Content-Security-Policy": [
|
|
1284
|
+
[
|
|
1285
|
+
"default-src 'self'",
|
|
1286
|
+
"script-src 'self'",
|
|
1287
|
+
"style-src 'self' 'unsafe-inline'",
|
|
1288
|
+
"img-src 'self' data: https:",
|
|
1289
|
+
"font-src 'self' data:",
|
|
1290
|
+
"connect-src 'self' https://api.example.com",
|
|
1291
|
+
"frame-ancestors 'none'",
|
|
1292
|
+
"base-uri 'self'",
|
|
1293
|
+
"form-action 'self'",
|
|
1294
|
+
].join("; "),
|
|
1295
|
+
],
|
|
1296
|
+
"X-Content-Type-Options": ["nosniff"],
|
|
1297
|
+
"X-Frame-Options": ["DENY"],
|
|
1298
|
+
"X-XSS-Protection": ["1; mode=block"],
|
|
1299
|
+
},
|
|
1300
|
+
});
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
// Permission request handler
|
|
1304
|
+
ses.setPermissionRequestHandler((webContents, permission, callback) => {
|
|
1305
|
+
const allowedPermissions: Electron.PermissionType[] = [
|
|
1306
|
+
"notifications",
|
|
1307
|
+
"clipboard-read",
|
|
1308
|
+
];
|
|
1309
|
+
|
|
1310
|
+
const denied: Electron.PermissionType[] = [
|
|
1311
|
+
"geolocation",
|
|
1312
|
+
"media",
|
|
1313
|
+
"mediaKeySystem",
|
|
1314
|
+
"midi",
|
|
1315
|
+
"pointerLock",
|
|
1316
|
+
"fullscreen",
|
|
1317
|
+
];
|
|
1318
|
+
|
|
1319
|
+
if (denied.includes(permission)) {
|
|
1320
|
+
console.warn(`Denied permission: ${permission}`);
|
|
1321
|
+
callback(false);
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
callback(allowedPermissions.includes(permission));
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
// Certificate error handler (for development, not production)
|
|
1329
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1330
|
+
ses.setCertificateVerifyProc((request, callback) => {
|
|
1331
|
+
// Reject invalid certificates in production
|
|
1332
|
+
if (request.errorCode !== 0) {
|
|
1333
|
+
console.error("Certificate error:", request.hostname);
|
|
1334
|
+
callback(-2); // Reject
|
|
1335
|
+
return;
|
|
1336
|
+
}
|
|
1337
|
+
callback(0); // Accept
|
|
1338
|
+
});
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
### Secure BrowserWindow Factory
|
|
1344
|
+
|
|
1345
|
+
```typescript
|
|
1346
|
+
// src/main/windows/secure-window.ts
|
|
1347
|
+
import { BrowserWindow, BrowserWindowConstructorOptions } from "electron";
|
|
1348
|
+
import { join } from "path";
|
|
1349
|
+
|
|
1350
|
+
export function createSecureWindow(
|
|
1351
|
+
options: BrowserWindowConstructorOptions = {}
|
|
1352
|
+
): BrowserWindow {
|
|
1353
|
+
const secureDefaults: BrowserWindowConstructorOptions = {
|
|
1354
|
+
webPreferences: {
|
|
1355
|
+
// Security: Isolate renderer from Node.js
|
|
1356
|
+
nodeIntegration: false,
|
|
1357
|
+
contextIsolation: true,
|
|
1358
|
+
sandbox: true,
|
|
1359
|
+
|
|
1360
|
+
// Security: Disable dangerous features
|
|
1361
|
+
webSecurity: true,
|
|
1362
|
+
allowRunningInsecureContent: false,
|
|
1363
|
+
experimentalFeatures: false,
|
|
1364
|
+
enableWebSQL: false,
|
|
1365
|
+
|
|
1366
|
+
// Security: Preload script for safe API exposure
|
|
1367
|
+
preload: join(__dirname, "../preload/index.js"),
|
|
1368
|
+
|
|
1369
|
+
// Security: Disable devtools in production
|
|
1370
|
+
devTools: process.env.NODE_ENV === "development",
|
|
1371
|
+
|
|
1372
|
+
// Performance: Disable unused features
|
|
1373
|
+
spellcheck: false,
|
|
1374
|
+
backgroundThrottling: true,
|
|
1375
|
+
},
|
|
1376
|
+
|
|
1377
|
+
// Security: Prevent title from showing sensitive data
|
|
1378
|
+
title: "My App",
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
return new BrowserWindow({
|
|
1382
|
+
...secureDefaults,
|
|
1383
|
+
...options,
|
|
1384
|
+
webPreferences: {
|
|
1385
|
+
...secureDefaults.webPreferences,
|
|
1386
|
+
...options.webPreferences,
|
|
1387
|
+
},
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
```
|
|
1391
|
+
|
|
1392
|
+
---
|
|
1393
|
+
|
|
1394
|
+
## Crash Reporting and Telemetry
|
|
1395
|
+
|
|
1396
|
+
### Crash Reporter Setup
|
|
1397
|
+
|
|
1398
|
+
```typescript
|
|
1399
|
+
// src/main/crash-reporter.ts
|
|
1400
|
+
import { crashReporter, app } from "electron";
|
|
1401
|
+
import { join } from "path";
|
|
1402
|
+
|
|
1403
|
+
export function initializeCrashReporter(): void {
|
|
1404
|
+
crashReporter.start({
|
|
1405
|
+
productName: app.getName(),
|
|
1406
|
+
companyName: "Your Company",
|
|
1407
|
+
submitURL: "https://your-crash-server.com/submit",
|
|
1408
|
+
uploadToServer: true,
|
|
1409
|
+
ignoreSystemCrashHandler: false,
|
|
1410
|
+
rateLimit: true,
|
|
1411
|
+
compress: true,
|
|
1412
|
+
extra: {
|
|
1413
|
+
version: app.getVersion(),
|
|
1414
|
+
platform: process.platform,
|
|
1415
|
+
arch: process.arch,
|
|
1416
|
+
},
|
|
1417
|
+
});
|
|
1418
|
+
|
|
1419
|
+
// Log crash reports location for debugging
|
|
1420
|
+
console.log("Crash reports path:", app.getPath("crashDumps"));
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
export function addCrashContext(key: string, value: string): void {
|
|
1424
|
+
crashReporter.addExtraParameter(key, value);
|
|
1425
|
+
}
|
|
1426
|
+
```
|
|
1427
|
+
|
|
1428
|
+
### Error Boundary in Main Process
|
|
1429
|
+
|
|
1430
|
+
```typescript
|
|
1431
|
+
// src/main/error-handler.ts
|
|
1432
|
+
import { dialog, app } from "electron";
|
|
1433
|
+
import log from "electron-log";
|
|
1434
|
+
|
|
1435
|
+
export function setupErrorHandlers(): void {
|
|
1436
|
+
// Unhandled promise rejections
|
|
1437
|
+
process.on("unhandledRejection", (reason, promise) => {
|
|
1438
|
+
log.error("Unhandled Rejection:", reason);
|
|
1439
|
+
|
|
1440
|
+
if (process.env.NODE_ENV === "development") {
|
|
1441
|
+
dialog.showErrorBox(
|
|
1442
|
+
"Unhandled Promise Rejection",
|
|
1443
|
+
String(reason)
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
// Uncaught exceptions
|
|
1449
|
+
process.on("uncaughtException", (error) => {
|
|
1450
|
+
log.error("Uncaught Exception:", error);
|
|
1451
|
+
|
|
1452
|
+
dialog.showErrorBox(
|
|
1453
|
+
"Application Error",
|
|
1454
|
+
`An unexpected error occurred: ${error.message}\n\nThe application will now quit.`
|
|
1455
|
+
);
|
|
1456
|
+
|
|
1457
|
+
app.quit();
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
// Renderer process crashes
|
|
1461
|
+
app.on("render-process-gone", (event, webContents, details) => {
|
|
1462
|
+
log.error("Renderer process gone:", details);
|
|
1463
|
+
|
|
1464
|
+
if (details.reason === "crashed") {
|
|
1465
|
+
const options = {
|
|
1466
|
+
type: "error" as const,
|
|
1467
|
+
title: "Window Crashed",
|
|
1468
|
+
message: "This window has crashed.",
|
|
1469
|
+
buttons: ["Reload", "Close"],
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
dialog.showMessageBox(options).then((result) => {
|
|
1473
|
+
if (result.response === 0) {
|
|
1474
|
+
webContents.reload();
|
|
1475
|
+
}
|
|
1476
|
+
});
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
// GPU process crashes
|
|
1481
|
+
app.on("child-process-gone", (event, details) => {
|
|
1482
|
+
log.error("Child process gone:", details);
|
|
1483
|
+
|
|
1484
|
+
if (details.type === "GPU" && details.reason === "crashed") {
|
|
1485
|
+
log.warn("GPU process crashed, app may have rendering issues");
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
}
|
|
1489
|
+
```
|
|
1490
|
+
|
|
1491
|
+
---
|
|
1492
|
+
|
|
1493
|
+
## Native Module Integration
|
|
1494
|
+
|
|
1495
|
+
### Rebuilding Native Modules
|
|
1496
|
+
|
|
1497
|
+
```json
|
|
1498
|
+
// package.json scripts
|
|
1499
|
+
{
|
|
1500
|
+
"scripts": {
|
|
1501
|
+
"postinstall": "electron-builder install-app-deps",
|
|
1502
|
+
"rebuild": "electron-rebuild -f -w better-sqlite3,keytar"
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
```
|
|
1506
|
+
|
|
1507
|
+
### Native Module Usage Pattern
|
|
1508
|
+
|
|
1509
|
+
```typescript
|
|
1510
|
+
// src/main/services/database.ts
|
|
1511
|
+
import Database from "better-sqlite3";
|
|
1512
|
+
import { app } from "electron";
|
|
1513
|
+
import { join } from "path";
|
|
1514
|
+
|
|
1515
|
+
export class DatabaseService {
|
|
1516
|
+
private db: Database.Database;
|
|
1517
|
+
|
|
1518
|
+
constructor() {
|
|
1519
|
+
const dbPath = join(app.getPath("userData"), "app.db");
|
|
1520
|
+
this.db = new Database(dbPath, {
|
|
1521
|
+
verbose: process.env.NODE_ENV === "development" ? console.log : undefined,
|
|
1522
|
+
});
|
|
1523
|
+
|
|
1524
|
+
// Enable WAL mode for better concurrency
|
|
1525
|
+
this.db.pragma("journal_mode = WAL");
|
|
1526
|
+
|
|
1527
|
+
// Initialize schema
|
|
1528
|
+
this.initializeSchema();
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
private initializeSchema(): void {
|
|
1532
|
+
this.db.exec(`
|
|
1533
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
1534
|
+
key TEXT PRIMARY KEY,
|
|
1535
|
+
value TEXT NOT NULL,
|
|
1536
|
+
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
|
|
1537
|
+
);
|
|
1538
|
+
|
|
1539
|
+
CREATE TABLE IF NOT EXISTS documents (
|
|
1540
|
+
id TEXT PRIMARY KEY,
|
|
1541
|
+
name TEXT NOT NULL,
|
|
1542
|
+
content TEXT,
|
|
1543
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
1544
|
+
updated_at INTEGER DEFAULT (strftime('%s', 'now'))
|
|
1545
|
+
);
|
|
1546
|
+
`);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
getSetting<T>(key: string): T | undefined {
|
|
1550
|
+
const row = this.db
|
|
1551
|
+
.prepare("SELECT value FROM settings WHERE key = ?")
|
|
1552
|
+
.get(key) as { value: string } | undefined;
|
|
1553
|
+
return row ? JSON.parse(row.value) : undefined;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
setSetting<T>(key: string, value: T): void {
|
|
1557
|
+
this.db
|
|
1558
|
+
.prepare(
|
|
1559
|
+
"INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES (?, ?, strftime('%s', 'now'))"
|
|
1560
|
+
)
|
|
1561
|
+
.run(key, JSON.stringify(value));
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
close(): void {
|
|
1565
|
+
this.db.close();
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
```
|
|
1569
|
+
|
|
1570
|
+
---
|
|
1571
|
+
|
|
1572
|
+
## Troubleshooting
|
|
1573
|
+
|
|
1574
|
+
### Common Issues
|
|
1575
|
+
|
|
1576
|
+
Issue: "Electron could not be found" error
|
|
1577
|
+
Symptoms: App fails to start, module not found
|
|
1578
|
+
Solution:
|
|
1579
|
+
|
|
1580
|
+
- Ensure electron is in devDependencies
|
|
1581
|
+
- Run npm rebuild electron
|
|
1582
|
+
- Check NODE_ENV is set correctly
|
|
1583
|
+
|
|
1584
|
+
Issue: White screen on launch
|
|
1585
|
+
Symptoms: Window opens but content doesn't load
|
|
1586
|
+
Solution:
|
|
1587
|
+
|
|
1588
|
+
- Check preload script path is correct
|
|
1589
|
+
- Verify loadFile/loadURL path
|
|
1590
|
+
- Enable devTools to see console errors
|
|
1591
|
+
- Check for CSP blocking scripts
|
|
1592
|
+
|
|
1593
|
+
Issue: IPC not working
|
|
1594
|
+
Symptoms: invoke returns undefined, no response
|
|
1595
|
+
Solution:
|
|
1596
|
+
|
|
1597
|
+
- Verify channel names match exactly
|
|
1598
|
+
- Check handler is registered before window loads
|
|
1599
|
+
- Ensure contextBridge is used correctly
|
|
1600
|
+
- Verify preload script is loaded
|
|
1601
|
+
|
|
1602
|
+
Issue: Native modules fail to load
|
|
1603
|
+
Symptoms: "Module was compiled against different Node.js version"
|
|
1604
|
+
Solution:
|
|
1605
|
+
|
|
1606
|
+
- Run electron-rebuild after npm install
|
|
1607
|
+
- Match Electron Node.js version
|
|
1608
|
+
- Use postinstall script for automatic rebuild
|
|
1609
|
+
|
|
1610
|
+
Issue: Auto-update not working
|
|
1611
|
+
Symptoms: No update notification, silent failure
|
|
1612
|
+
Solution:
|
|
1613
|
+
|
|
1614
|
+
- Check app is signed (required for updates)
|
|
1615
|
+
- Verify publish configuration
|
|
1616
|
+
- Check network/firewall settings
|
|
1617
|
+
- Enable electron-updater logging
|
|
1618
|
+
|
|
1619
|
+
---
|
|
1620
|
+
|
|
1621
|
+
## External Resources
|
|
1622
|
+
|
|
1623
|
+
### Official Documentation
|
|
1624
|
+
|
|
1625
|
+
- Electron Documentation: https://www.electronjs.org/docs
|
|
1626
|
+
- Electron Forge: https://www.electronforge.io/
|
|
1627
|
+
- Electron Builder: https://www.electron.build/
|
|
1628
|
+
|
|
1629
|
+
### Security
|
|
1630
|
+
|
|
1631
|
+
- Security Checklist: https://www.electronjs.org/docs/tutorial/security
|
|
1632
|
+
- Context Isolation: https://www.electronjs.org/docs/tutorial/context-isolation
|
|
1633
|
+
|
|
1634
|
+
### Build & Distribution
|
|
1635
|
+
|
|
1636
|
+
- Code Signing: https://www.electronjs.org/docs/tutorial/code-signing
|
|
1637
|
+
- Auto Updates: https://www.electronjs.org/docs/tutorial/updates
|
|
1638
|
+
- macOS Notarization: https://www.electronjs.org/docs/tutorial/mac-app-store-submission-guide
|
|
1639
|
+
|
|
1640
|
+
### Testing
|
|
1641
|
+
|
|
1642
|
+
- Playwright: https://playwright.dev/
|
|
1643
|
+
- Testing Guide: https://www.electronjs.org/docs/tutorial/testing-on-headless-ci
|
|
1644
|
+
|
|
1645
|
+
---
|
|
1646
|
+
|
|
1647
|
+
Version: 1.1.0
|
|
1648
|
+
Last Updated: 2026-01-10
|
|
1649
|
+
Changes: Added Utility Process patterns, Protocol Handlers, Deep Linking, OWASP-aligned Security Hardening, Crash Reporting, Native Module Integration
|