fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
- Remove middleware reference from Gitea Traefik labels (caused routing issues) - Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s) - Add explicit service reference in Traefik labels - Fix intermittent 504 timeouts by improving PostgreSQL connection handling Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
1087
docs/ARCHITECTURE-DECISION.md
Normal file
1087
docs/ARCHITECTURE-DECISION.md
Normal file
File diff suppressed because it is too large
Load Diff
731
docs/DOCUMENTATION-ANALYSIS.md
Normal file
731
docs/DOCUMENTATION-ANALYSIS.md
Normal file
@@ -0,0 +1,731 @@
|
||||
# Markdown Files Analysis - Custom PHP Framework
|
||||
|
||||
**Generated:** $(date)
|
||||
**Total Project Files:** 1579 (including dependencies)
|
||||
**Relevant Files:** 513 (excluding node_modules, vendor, .git)
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This analysis covers all markdown files in the Custom PHP Framework project, excluding third-party dependencies. The goal is to reorganize documentation from AI-specific `/docs/claude/` to general `/docs/` structure.
|
||||
|
||||
### Key Statistics
|
||||
- **Total Markdown Files:** 513
|
||||
- **Documentation Files:** ~100 (estimated, in /docs/)
|
||||
- **Source Code Docs:** ~50 (README files in /src/)
|
||||
- **Test Documentation:** ~20 (test strategy, examples)
|
||||
- **Configuration Docs:** ~10 (deployment, docker, ansible)
|
||||
- **Dependencies:** 1066 (node_modules + vendor - excluded)
|
||||
|
||||
---
|
||||
|
||||
## File Distribution by Directory
|
||||
|
||||
|
||||
```
|
||||
63 ./docs/claude
|
||||
57 ./docs
|
||||
48 ./docs/deployment
|
||||
17 ./src/Framework/View/Processors/docs
|
||||
15 ./backups/docs-backup-20250731125004
|
||||
14 ./.claude/agents
|
||||
13 ./docs/livecomponents
|
||||
12 ./.gitea/workflows
|
||||
11 ./docs/planning
|
||||
9 .
|
||||
7 ./docs/guides
|
||||
6 ./src/Framework/LiveComponents/docs
|
||||
6 ./deployment/docs/tests
|
||||
6 ./backups/docs-backup-20250731125004/framework
|
||||
5 ./tests/e2e/livecomponents
|
||||
5 ./resources/js/docs
|
||||
5 ./deployment/wireguard
|
||||
5 ./deployment/docs/status
|
||||
5 ./deployment/docs/guides
|
||||
5 ./backups/docs-backup-20250731125004/framework/analytics
|
||||
4 ./src/Framework/LiveComponents
|
||||
4 ./docs/todo
|
||||
4 ./docs/contributing
|
||||
4 ./docs/components/waf
|
||||
4 ./docs/components/security
|
||||
4 ./docs/components/cryptography
|
||||
4 ./docs/components/auth
|
||||
4 ./backups/docs-backup-20250731125004/design-system
|
||||
3 ./src/Framework/ReflectionLegacy
|
||||
3 ./docs/roadmap
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Detailed File Listing by Category
|
||||
|
||||
### 1. Documentation Directory (/docs/)
|
||||
|
||||
- `./docs/admin-design-system.md` (479 lines)
|
||||
- `./docs/ADMIN-FORM-FIELDS-DESIGN.md` (440 lines)
|
||||
- `./docs/ADMIN-FORM-FIELDS-USAGE.md` (491 lines)
|
||||
- `./docs/api/index.md` (838 lines)
|
||||
- `./docs/api.md` (8 lines)
|
||||
- `./docs/api-versioning-examples.md` (294 lines)
|
||||
- `./docs/architecture/components.md` (528 lines)
|
||||
- `./docs/ARCHITECTURE_IMPROVEMENTS.md` (1981 lines)
|
||||
- `./docs/architecture/overview.md` (226 lines)
|
||||
- `./docs/architecture/patterns.md` (1137 lines)
|
||||
- `./docs/batch-api-examples.md` (236 lines)
|
||||
- `./docs/cache-warming.md` (699 lines)
|
||||
- `./docs/claude/animationsystem.md` (585 lines)
|
||||
- `./docs/claude/architecture.md` (189 lines)
|
||||
- `./docs/claude/async-components.md` (26 lines)
|
||||
- `./docs/claude/chips-cookies.md` (465 lines)
|
||||
- `./docs/claude/common-workflows.md` (1259 lines)
|
||||
- `./docs/claude/configuration-best-practices.md` (297 lines)
|
||||
- `./docs/claude/console-commands.md` (30 lines)
|
||||
- `./docs/claude/curl-oop-api.md` (996 lines)
|
||||
- `./docs/claude/database-patterns.md` (1526 lines)
|
||||
- `./docs/claude/deployment-architecture.md` (777 lines)
|
||||
- `./docs/claude/development-commands.md` (157 lines)
|
||||
- `./docs/claude/error-handling.md` (644 lines)
|
||||
- `./docs/claude/event-system.md` (963 lines)
|
||||
- `./docs/claude/examples/migrations/SafeVsUnsafeMigrations.md` (420 lines)
|
||||
- `./docs/claude/exception-system-advanced-features.md` (754 lines)
|
||||
- `./docs/claude/filesystem-patterns.md` (965 lines)
|
||||
- `./docs/claude/framework-personas.md` (766 lines)
|
||||
- `./docs/claude/framework-refactoring-recommendations.md` (748 lines)
|
||||
- `./docs/claude/guidelines.md` (882 lines)
|
||||
- `./docs/claude/livecomponent-file-uploads.md` (893 lines)
|
||||
- `./docs/claude/livecomponent-formbuilder-usage.md` (458 lines)
|
||||
- `./docs/claude/livecomponent-lazy-loading.md` (681 lines)
|
||||
- `./docs/claude/livecomponent-lifecycle-hooks.md` (572 lines)
|
||||
- `./docs/claude/livecomponent-nested-components.md` (717 lines)
|
||||
- `./docs/claude/livecomponents-best-practices.md` (461 lines)
|
||||
- `./docs/claude/livecomponents-caching-system.md` (683 lines)
|
||||
- `./docs/claude/livecomponents-dom-badges.md` (236 lines)
|
||||
- `./docs/claude/livecomponent-security.md` (223 lines)
|
||||
- `./docs/claude/livecomponents-implementation-plan.md` (699 lines)
|
||||
- `./docs/claude/livecomponents-lazy-loading.md` (717 lines)
|
||||
- `./docs/claude/livecomponent-slot-system.md` (1127 lines)
|
||||
- `./docs/claude/livecomponents-monitoring-debugging.md` (701 lines)
|
||||
- `./docs/claude/livecomponents-observability.md` (540 lines)
|
||||
- `./docs/claude/livecomponents-performance-optimizations.md` (725 lines)
|
||||
- `./docs/claude/livecomponents-playground.md` (1108 lines)
|
||||
- `./docs/claude/livecomponents-roadmap.md` (1027 lines)
|
||||
- `./docs/claude/livecomponents-system.md` (552 lines)
|
||||
- `./docs/claude/livecomponents-test-harness.md` (504 lines)
|
||||
- `./docs/claude/magiclinks-system.md` (807 lines)
|
||||
- `./docs/claude/mcp-integration.md` (260 lines)
|
||||
- `./docs/claude/migration-system-quick-reference.md` (202 lines)
|
||||
- `./docs/claude/ml-framework-architecture.md` (593 lines)
|
||||
- `./docs/claude/naming-conventions.md` (832 lines)
|
||||
- `./docs/claude/performance-monitoring.md` (30 lines)
|
||||
- `./docs/claude/performance-profiling.md` (857 lines)
|
||||
- `./docs/claude/php85-framework-integration.md` (550 lines)
|
||||
- `./docs/claude/posix-system.md` (1316 lines)
|
||||
- `./docs/claude/postgresql-features.md` (1127 lines)
|
||||
- `./docs/claude/queue-system.md` (1135 lines)
|
||||
- `./docs/claude/route-authorization.md` (291 lines)
|
||||
- `./docs/claude/routing-value-objects.md` (305 lines)
|
||||
- `./docs/claude/scheduler-queue-pipeline.md` (458 lines)
|
||||
- `./docs/claude/scheduler-queue-pipeline-persona.md` (273 lines)
|
||||
- `./docs/claude/security-patterns.md` (1135 lines)
|
||||
- `./docs/claude/sockets-module.md` (671 lines)
|
||||
- `./docs/claude/sse-integration-guide.md` (978 lines)
|
||||
- `./docs/claude/sse-system.md` (1068 lines)
|
||||
- `./docs/claude/structured-logging.md` (577 lines)
|
||||
- `./docs/claude/troubleshooting.md` (1074 lines)
|
||||
- `./docs/claude/typed-string-system.md` (1141 lines)
|
||||
- `./docs/claude/view-caching-system.md` (716 lines)
|
||||
- `./docs/claude/view-refactoring-plan.md` (614 lines)
|
||||
- `./docs/claude/xcomponent-processor.md` (764 lines)
|
||||
- `./docs/claude/x-component-syntax.md` (335 lines)
|
||||
- `./docs/components/analytics/configuration.md` (477 lines)
|
||||
- `./docs/components/analytics/examples.md` (760 lines)
|
||||
- `./docs/components/analytics/index.md` (507 lines)
|
||||
- `./docs/components/auth/configuration.md` (429 lines)
|
||||
- `./docs/components/auth/examples.md` (796 lines)
|
||||
- `./docs/components/auth/index.md` (309 lines)
|
||||
- `./docs/components/auth/security.md` (903 lines)
|
||||
- `./docs/components/cryptography/configuration.md` (396 lines)
|
||||
- `./docs/components/cryptography/examples.md` (702 lines)
|
||||
- `./docs/components/cryptography/index.md` (364 lines)
|
||||
- `./docs/components/cryptography/security.md` (727 lines)
|
||||
- `./docs/components/security/csrf-protection.md` (417 lines)
|
||||
- `./docs/components/security/index.md` (363 lines)
|
||||
- `./docs/components/security/request-signing.md` (410 lines)
|
||||
- `./docs/components/security/security-headers.md` (382 lines)
|
||||
- `./docs/components/validation/examples.md` (424 lines)
|
||||
- `./docs/components/validation/index.md` (338 lines)
|
||||
- `./docs/components/validation/rules.md` (451 lines)
|
||||
- `./docs/components/waf/configuration.md` (279 lines)
|
||||
- `./docs/components/waf/feedback.md` (280 lines)
|
||||
- `./docs/components/waf/index.md` (135 lines)
|
||||
- `./docs/components/waf/machine-learning.md` (252 lines)
|
||||
- `./docs/console-best-practices.md` (486 lines)
|
||||
- `./docs/console-dialog-mode.md` (472 lines)
|
||||
- `./docs/console-optional-parameters.md` (422 lines)
|
||||
- `./docs/contributing/code-style.md` (598 lines)
|
||||
- `./docs/contributing/documentation.md` (345 lines)
|
||||
- `./docs/contributing/git-hooks.md` (151 lines)
|
||||
- `./docs/contributing/pull-requests.md` (334 lines)
|
||||
- `./docs/database-migration-fix.md` (78 lines)
|
||||
- `./docs/database-module-migration-analysis.md` (413 lines)
|
||||
- `./docs/database/new-features.md` (329 lines)
|
||||
- `./docs/DefaultImplementation-Attribute.md` (447 lines)
|
||||
- `./docs/dependency-scanning.md` (401 lines)
|
||||
- `./docs/deployment/ANSIBLE_DEPLOYMENT.md` (969 lines)
|
||||
- `./docs/deployment/AUTOSSH-SETUP-COMPLETED.md` (243 lines)
|
||||
- `./docs/deployment/AUTOSSH-SETUP.md` (428 lines)
|
||||
- `./docs/deployment/cache-configuration.md` (244 lines)
|
||||
- `./docs/deployment/cache-permissions-quick-fix.md` (74 lines)
|
||||
- `./docs/deployment/database-migration-strategy.md` (775 lines)
|
||||
- `./docs/deployment/deployment-automation.md` (816 lines)
|
||||
- `./docs/deployment/env-production-template.md` (407 lines)
|
||||
- `./docs/deployment/gitea-secrets-setup.md` (297 lines)
|
||||
- `./docs/deployment/GRAFANA-VPN-ACCESS.md` (108 lines)
|
||||
- `./docs/deployment/GRAFANA-VPN-NEXT-STEPS.md` (125 lines)
|
||||
- `./docs/deployment/GRAFANA-VPN-SECURITY-PLAN.md` (149 lines)
|
||||
- `./docs/deployment/local-secrets-unification-plan.md` (470 lines)
|
||||
- `./docs/deployment/local-stack-restructure-plan.md` (359 lines)
|
||||
- `./docs/deployment/logging-configuration.md` (715 lines)
|
||||
- `./docs/deployment/production-compose-consolidation-plan.md` (262 lines)
|
||||
- `./docs/deployment/production-logging.md` (601 lines)
|
||||
- `./docs/deployment/production-prerequisites.md` (340 lines)
|
||||
- `./docs/deployment/PRODUCTION-SECURITY-UPDATES.md` (133 lines)
|
||||
- `./docs/deployment/README.md` (67 lines)
|
||||
- `./docs/deployment/secrets-management.md` (614 lines)
|
||||
- `./docs/deployment/shared-postgres-override-plan.md` (303 lines)
|
||||
- `./docs/deployment/SSH-MAKEFILE-COMMANDS.md` (319 lines)
|
||||
- `./docs/deployment/SSL-PRODUCTION-SETUP.md` (194 lines)
|
||||
- `./docs/deployment/ssl-setup.md` (540 lines)
|
||||
- `./docs/deployment/staging-database-connection-analysis.md` (185 lines)
|
||||
- `./docs/deployment/staging-postgres-connection-plan.md` (209 lines)
|
||||
- `./docs/deployment/test-registry-credentials.md` (197 lines)
|
||||
- `./docs/deployment/VPN-ROUTING-CLIENT-CHECK.md` (195 lines)
|
||||
- `./docs/deployment/VPN-ROUTING-FINAL-FIX.md` (91 lines)
|
||||
- `./docs/deployment/VPN-ROUTING-FIX-DETAILED.md` (100 lines)
|
||||
- `./docs/deployment/VPN-ROUTING-FIX-GUIDE.md` (143 lines)
|
||||
- `./docs/deployment/VPN-ROUTING-FIX.md` (120 lines)
|
||||
- `./docs/deployment/VPN-ROUTING-PROBLEM-ANALYSIS.md` (120 lines)
|
||||
- `./docs/deployment/WIREGUARD-DNS-FIX-IMPLEMENTED.md` (283 lines)
|
||||
- `./docs/deployment/WIREGUARD-FUTURE-SECURITY.md` (243 lines)
|
||||
- `./docs/deployment/WIREGUARD-IMPLEMENTATION-PLAN.md` (1175 lines)
|
||||
- `./docs/deployment/WIREGUARD-NEW-CONFIG-READY.md` (123 lines)
|
||||
- `./docs/deployment/WIREGUARD-RECREATE-AND-TEST.md` (176 lines)
|
||||
- `./docs/deployment/WIREGUARD-SETUP.md` (595 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-CONFIG-EXAMPLE.md` (48 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-DNS-FIX.md` (141 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-DNS-PRIORITY-FIX.md` (108 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-FIREWALL-CHECK.md` (186 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-INTERFACE-METRIC-FIX.md` (63 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-ROUTE-FIX.md` (103 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-ROUTE-GATEWAY-FIX.md` (176 lines)
|
||||
- `./docs/deployment/WIREGUARD-WINDOWS-ROUTING-FINAL-ANALYSIS.md` (109 lines)
|
||||
- `./docs/design-token-system.md` (374 lines)
|
||||
- `./docs/error-handler-enhancements.md` (615 lines)
|
||||
- `./docs/ERROR-HANDLING-AUDIT-REPORT.md` (708 lines)
|
||||
- `./docs/ERROR-HANDLING-MIGRATION-PLAN.md` (3319 lines)
|
||||
- `./docs/ERROR-HANDLING-UNIFIED-ARCHITECTURE.md` (1766 lines)
|
||||
- `./docs/ERROR-RENDERER-IMPROVEMENTS-PLAN.md` (274 lines)
|
||||
- `./docs/ERROR-RENDERER-REFACTORING-PLAN.md` (199 lines)
|
||||
- `./docs/examples/poll-system-usage.md` (609 lines)
|
||||
- `./docs/exception-hierarchy-pattern.md` (684 lines)
|
||||
- `./docs/exception-migration-guide.md` (1132 lines)
|
||||
- `./docs/FILECACHE_PERMISSION_FIX_PLAN.md` (217 lines)
|
||||
- `./docs/FILEPERMISSION_EXCEPTION_IMPROVEMENT_PLAN.md` (226 lines)
|
||||
- `./docs/FORPROCESSOR-FIX-SUMMARY.md` (190 lines)
|
||||
- `./docs/FORPROCESSOR-ISSUE-ANALYSIS.md` (106 lines)
|
||||
- `./docs/FRAMEWORK-IMPROVEMENT-PROPOSALS.md` (591 lines)
|
||||
- `./docs/FRAMEWORK_IMPROVEMENTS_ROADMAP.md` (173 lines)
|
||||
- `./docs/getting-started/configuration.md` (214 lines)
|
||||
- `./docs/getting-started/first-steps.md` (327 lines)
|
||||
- `./docs/getting-started/installation.md` (108 lines)
|
||||
- `./docs/gitea-actions-setup.md` (383 lines)
|
||||
- `./docs/graphql-examples.md` (386 lines)
|
||||
- `./docs/guides/controllers.md` (727 lines)
|
||||
- `./docs/guides/README-image-upload.md` (128 lines)
|
||||
- `./docs/guides/README-static-site.md` (118 lines)
|
||||
- `./docs/guides/README-websocket.md` (118 lines)
|
||||
- `./docs/guides/routing.md` (370 lines)
|
||||
- `./docs/guides/security.md` (585 lines)
|
||||
- `./docs/guides/validation.md` (539 lines)
|
||||
- `./docs/implementations/queue-job-anomaly-detection.md` (634 lines)
|
||||
- `./docs/job-dashboard.md` (733 lines)
|
||||
- `./docs/livecomponents/01-getting-started.md` (520 lines)
|
||||
- `./docs/livecomponents/advanced-features.md` (1027 lines)
|
||||
- `./docs/livecomponents/api-reference.md` (1331 lines)
|
||||
- `./docs/livecomponents/attributes-reference.md` (1380 lines)
|
||||
- `./docs/livecomponents/best-practices.md` (1014 lines)
|
||||
- `./docs/livecomponents/events-reference.md` (1354 lines)
|
||||
- `./docs/livecomponents/faq.md` (864 lines)
|
||||
- `./docs/livecomponents/LIVECOMPONENTS-COMPLETE-SUMMARY.md` (681 lines)
|
||||
- `./docs/livecomponents/performance-guide.md` (1003 lines)
|
||||
- `./docs/livecomponents/PHASE-5-COMPLETION.md` (597 lines)
|
||||
- `./docs/livecomponents/README.md` (257 lines)
|
||||
- `./docs/livecomponents/security-guide.md` (691 lines)
|
||||
- `./docs/livecomponents/troubleshooting.md` (978 lines)
|
||||
- `./docs/livecomponents-type-safety-refactoring.md` (400 lines)
|
||||
- `./docs/logging/best-practices.md` (837 lines)
|
||||
- `./docs/logging/docker-json-logging.md` (373 lines)
|
||||
- `./docs/logging/log-health-check-command.md` (162 lines)
|
||||
- `./docs/logging-module.md` (185 lines)
|
||||
- `./docs/Makefile.md` (66 lines)
|
||||
- `./docs/middleware-robustness.md` (130 lines)
|
||||
- `./docs/migration/ErrorHandling-to-ExceptionHandling-Strategy.md` (798 lines)
|
||||
- `./docs/ml-model-management-deployment.md` (807 lines)
|
||||
- `./docs/ml-model-management.md` (872 lines)
|
||||
- `./docs/next-steps.md` (67 lines)
|
||||
- `./docs/n-plus-one-detection.md` (749 lines)
|
||||
- `./docs/performance/filesystem-optimization-analysis.md` (284 lines)
|
||||
- `./docs/performance/filesystem-phase1-optimizations-implemented.md` (367 lines)
|
||||
- `./docs/performance/index-optimization.md` (559 lines)
|
||||
- `./docs/PERMISSIONS.md` (226 lines)
|
||||
- `./docs/plan.md` (252 lines)
|
||||
- `./docs/planning/ADMIN_AUTH_SECURITY_ENHANCEMENT.md` (140 lines)
|
||||
- `./docs/planning/CURRENT_SPRINT.md` (178 lines)
|
||||
- `./docs/planning/docs-implementation-plan.md` (163 lines)
|
||||
- `./docs/planning/docs-replacement-recommendation.md` (137 lines)
|
||||
- `./docs/planning/documentation-update-proposal.md` (138 lines)
|
||||
- `./docs/planning/ML-WAF-Behavioral-Analysis-Implementation-Summary.md` (565 lines)
|
||||
- `./docs/planning/N+1-Detection-ML-Implementation-Summary.md` (263 lines)
|
||||
- `./docs/planning/N+1-Detection-ML-Integration-Summary.md` (402 lines)
|
||||
- `./docs/planning/N+1-Detection-ML-Next-Steps.md` (1154 lines)
|
||||
- `./docs/planning/SECURITY_AUDIT_REPORT.md` (1059 lines)
|
||||
- `./docs/planning/TODO.md` (425 lines)
|
||||
- `./docs/PRESAVE-USAGE-GUIDE.md` (458 lines)
|
||||
- `./docs/quality/code-quality-scanner.md` (112 lines)
|
||||
- `./docs/queue-deployment.md` (631 lines)
|
||||
- `./docs/README.md` (68 lines)
|
||||
- `./docs/REDIS_ACL_EXPLANATION.md` (388 lines)
|
||||
- `./docs/REDIS_AUTHENTICATION_ANALYSIS.md` (364 lines)
|
||||
- `./docs/refactoring/mcp-module-refactoring-plan.md` (346 lines)
|
||||
- `./docs/refactoring/phase-1-completion-report.md` (213 lines)
|
||||
- `./docs/refactoring/phase-2-completion-report.md` (324 lines)
|
||||
- `./docs/roadmap/features.md` (83 lines)
|
||||
- `./docs/roadmap/milestones.md` (289 lines)
|
||||
- `./docs/roadmap/tasks.md` (138 lines)
|
||||
- `./docs/search-api-examples.md` (471 lines)
|
||||
- `./docs/search-external-mapping-examples.md` (255 lines)
|
||||
- `./docs/spa-router-backend-integration.md` (125 lines)
|
||||
- `./docs/sqlstate-integration-design.md` (777 lines)
|
||||
- `./docs/SSL_DEPLOYMENT.md` (371 lines)
|
||||
- `./docs/tasks.md` (153 lines)
|
||||
- `./docs/TEMPLATE-SYSTEM-ANALYSIS.md` (386 lines)
|
||||
- `./docs/todo/css-generation-system.md` (293 lines)
|
||||
- `./docs/todo/design-system-improvements.md` (263 lines)
|
||||
- `./docs/todo/design-token-system-future-features.md` (318 lines)
|
||||
- `./docs/todo/README.md` (68 lines)
|
||||
- `./docs/troubleshooting/AUTOLOADER_WORKAROUND.md` (67 lines)
|
||||
- `./docs/vault-system.md` (401 lines)
|
||||
- `./docs/whatsapp-notification-channel.md` (438 lines)
|
||||
|
||||
### 2. Claude-Specific Documentation (/docs/claude/)
|
||||
|
||||
- `./docs/claude/animationsystem.md` (585 lines)
|
||||
- `./docs/claude/architecture.md` (189 lines)
|
||||
- `./docs/claude/async-components.md` (26 lines)
|
||||
- `./docs/claude/chips-cookies.md` (465 lines)
|
||||
- `./docs/claude/common-workflows.md` (1259 lines)
|
||||
- `./docs/claude/configuration-best-practices.md` (297 lines)
|
||||
- `./docs/claude/console-commands.md` (30 lines)
|
||||
- `./docs/claude/curl-oop-api.md` (996 lines)
|
||||
- `./docs/claude/database-patterns.md` (1526 lines)
|
||||
- `./docs/claude/deployment-architecture.md` (777 lines)
|
||||
- `./docs/claude/development-commands.md` (157 lines)
|
||||
- `./docs/claude/error-handling.md` (644 lines)
|
||||
- `./docs/claude/event-system.md` (963 lines)
|
||||
- `./docs/claude/examples/migrations/SafeVsUnsafeMigrations.md` (420 lines)
|
||||
- `./docs/claude/exception-system-advanced-features.md` (754 lines)
|
||||
- `./docs/claude/filesystem-patterns.md` (965 lines)
|
||||
- `./docs/claude/framework-personas.md` (766 lines)
|
||||
- `./docs/claude/framework-refactoring-recommendations.md` (748 lines)
|
||||
- `./docs/claude/guidelines.md` (882 lines)
|
||||
- `./docs/claude/livecomponent-file-uploads.md` (893 lines)
|
||||
- `./docs/claude/livecomponent-formbuilder-usage.md` (458 lines)
|
||||
- `./docs/claude/livecomponent-lazy-loading.md` (681 lines)
|
||||
- `./docs/claude/livecomponent-lifecycle-hooks.md` (572 lines)
|
||||
- `./docs/claude/livecomponent-nested-components.md` (717 lines)
|
||||
- `./docs/claude/livecomponents-best-practices.md` (461 lines)
|
||||
- `./docs/claude/livecomponents-caching-system.md` (683 lines)
|
||||
- `./docs/claude/livecomponents-dom-badges.md` (236 lines)
|
||||
- `./docs/claude/livecomponent-security.md` (223 lines)
|
||||
- `./docs/claude/livecomponents-implementation-plan.md` (699 lines)
|
||||
- `./docs/claude/livecomponents-lazy-loading.md` (717 lines)
|
||||
- `./docs/claude/livecomponent-slot-system.md` (1127 lines)
|
||||
- `./docs/claude/livecomponents-monitoring-debugging.md` (701 lines)
|
||||
- `./docs/claude/livecomponents-observability.md` (540 lines)
|
||||
- `./docs/claude/livecomponents-performance-optimizations.md` (725 lines)
|
||||
- `./docs/claude/livecomponents-playground.md` (1108 lines)
|
||||
- `./docs/claude/livecomponents-roadmap.md` (1027 lines)
|
||||
- `./docs/claude/livecomponents-system.md` (552 lines)
|
||||
- `./docs/claude/livecomponents-test-harness.md` (504 lines)
|
||||
- `./docs/claude/magiclinks-system.md` (807 lines)
|
||||
- `./docs/claude/mcp-integration.md` (260 lines)
|
||||
- `./docs/claude/migration-system-quick-reference.md` (202 lines)
|
||||
- `./docs/claude/ml-framework-architecture.md` (593 lines)
|
||||
- `./docs/claude/naming-conventions.md` (832 lines)
|
||||
- `./docs/claude/performance-monitoring.md` (30 lines)
|
||||
- `./docs/claude/performance-profiling.md` (857 lines)
|
||||
- `./docs/claude/php85-framework-integration.md` (550 lines)
|
||||
- `./docs/claude/posix-system.md` (1316 lines)
|
||||
- `./docs/claude/postgresql-features.md` (1127 lines)
|
||||
- `./docs/claude/queue-system.md` (1135 lines)
|
||||
- `./docs/claude/route-authorization.md` (291 lines)
|
||||
- `./docs/claude/routing-value-objects.md` (305 lines)
|
||||
- `./docs/claude/scheduler-queue-pipeline.md` (458 lines)
|
||||
- `./docs/claude/scheduler-queue-pipeline-persona.md` (273 lines)
|
||||
- `./docs/claude/security-patterns.md` (1135 lines)
|
||||
- `./docs/claude/sockets-module.md` (671 lines)
|
||||
- `./docs/claude/sse-integration-guide.md` (978 lines)
|
||||
- `./docs/claude/sse-system.md` (1068 lines)
|
||||
- `./docs/claude/structured-logging.md` (577 lines)
|
||||
- `./docs/claude/troubleshooting.md` (1074 lines)
|
||||
- `./docs/claude/typed-string-system.md` (1141 lines)
|
||||
- `./docs/claude/view-caching-system.md` (716 lines)
|
||||
- `./docs/claude/view-refactoring-plan.md` (614 lines)
|
||||
- `./docs/claude/xcomponent-processor.md` (764 lines)
|
||||
- `./docs/claude/x-component-syntax.md` (335 lines)
|
||||
|
||||
### 3. Root Directory Documentation
|
||||
|
||||
- `./AGENTS.md` (432 lines)
|
||||
- `./CLAUDE.md` (292 lines)
|
||||
- `./CLEANUP_PLAN.md` (186 lines)
|
||||
- `./DEPLOYMENT_FIX.md` (22 lines)
|
||||
- `./ENV_SETUP.md` (266 lines)
|
||||
- `./LIVECOMPONENTS_TEST.md` (160 lines)
|
||||
- `./README.md` (322 lines)
|
||||
- `./SRC_STRUCTURE_IMPROVEMENTS.md` (368 lines)
|
||||
- `./todo.md` (585 lines)
|
||||
|
||||
### 4. Source Code Documentation
|
||||
|
||||
- `./src/Application/Controllers/Test/README.md` (188 lines)
|
||||
- `./src/Application/LiveComponents/UserRegistration/README.md` (213 lines)
|
||||
- `./src/Domain/Asset/README.md` (526 lines)
|
||||
- `./src/Domain/Cms/README.md` (193 lines)
|
||||
- `./src/Domain/Media/README.md` (92 lines)
|
||||
- `./src/Domain/Meta/README.md` (18 lines)
|
||||
- `./src/Domain/PreSave/README.md` (629 lines)
|
||||
- `./src/Framework/Admin/README.md` (691 lines)
|
||||
- `./src/Framework/Analytics/README.md` (342 lines)
|
||||
- `./src/Framework/AsyncExamples/README.md` (58 lines)
|
||||
- `./src/Framework/Cache/README.md` (338 lines)
|
||||
- `./src/Framework/CircuitBreaker/README.md` (210 lines)
|
||||
- `./src/Framework/Console/README.md` (131 lines)
|
||||
- `./src/Framework/Core/DOKUMENTATION.md` (45 lines)
|
||||
- `./src/Framework/Core/ImplementationLocator.md` (17 lines)
|
||||
- `./src/Framework/Core/ValueObjects/README.md` (334 lines)
|
||||
- `./src/Framework/Database/Example/README.md` (69 lines)
|
||||
- `./src/Framework/Database/README.md` (21 lines)
|
||||
- `./src/Framework/Database/TODO.md` (92 lines)
|
||||
- `./src/Framework/DDoS/README.md` (133 lines)
|
||||
- `./src/Framework/DDoS/REFACTORING-SUMMARY.md` (130 lines)
|
||||
- `./src/Framework/Debug/readme.md` (104 lines)
|
||||
- `./src/Framework/DI/DOKUMENTATION.md` (90 lines)
|
||||
- `./src/Framework/Discovery/README.md` (184 lines)
|
||||
- `./src/Framework/Display/README.md` (583 lines)
|
||||
- `./src/Framework/ErrorBoundaries/Async/README.md` (265 lines)
|
||||
- `./src/Framework/ErrorBoundaries/Middleware/README.md` (222 lines)
|
||||
- `./src/Framework/EventSourcing/Projections/README.md` (394 lines)
|
||||
- `./src/Framework/EventSourcing/Replay/README.md` (1035 lines)
|
||||
- `./src/Framework/EventSourcing/Sagas/README.md` (863 lines)
|
||||
- `./src/Framework/EventSourcing/Snapshots/README.md` (703 lines)
|
||||
- `./src/Framework/Exception/InErrorHandler.md` (22 lines)
|
||||
- `./src/Framework/Filesystem/README.md` (68 lines)
|
||||
- `./src/Framework/Http/DOKUMENTATION.md` (92 lines)
|
||||
- `./src/Framework/Http/Middlewares/REFACTORING-SUMMARY.md` (144 lines)
|
||||
- `./src/Framework/Http/Url.php85/README.md` (373 lines)
|
||||
- `./src/Framework/Http/Url/README.md` (373 lines)
|
||||
- `./src/Framework/LiveComponents/Cache/README.md` (352 lines)
|
||||
- `./src/Framework/LiveComponents/docs/API-REFERENCE.md` (1369 lines)
|
||||
- `./src/Framework/LiveComponents/docs/COMPONENT-CREATION-GUIDE.md` (1114 lines)
|
||||
- `./src/Framework/LiveComponents/docs/DEVTOOLS-DEBUGGING-GUIDE.md` (1163 lines)
|
||||
- `./src/Framework/LiveComponents/docs/PERFORMANCE-GUIDE.md` (1023 lines)
|
||||
- `./src/Framework/LiveComponents/docs/SECURITY-GUIDE.md` (1073 lines)
|
||||
- `./src/Framework/LiveComponents/docs/UPLOAD-GUIDE.md` (953 lines)
|
||||
- `./src/Framework/LiveComponents/ERROR-RECOVERY.md` (615 lines)
|
||||
- `./src/Framework/LiveComponents/MEMORY-MANAGEMENT.md` (264 lines)
|
||||
- `./src/Framework/LiveComponents/README.md` (75 lines)
|
||||
- `./src/Framework/LiveComponents/TODO.md` (1216 lines)
|
||||
- `./src/Framework/Mcp/README.md` (224 lines)
|
||||
- `./src/Framework/Mcp/Tools/Categories/Analysis/README.md` (19 lines)
|
||||
- `./src/Framework/Mcp/Tools/Categories/Database/README.md` (37 lines)
|
||||
- `./src/Framework/Mcp/Tools/Categories/Development/README.md` (30 lines)
|
||||
- `./src/Framework/Mcp/Tools/Categories/Performance/README.md` (36 lines)
|
||||
- `./src/Framework/Mcp/Tools/Categories/README.md` (70 lines)
|
||||
- `./src/Framework/Mcp/Tools/Categories/Security/README.md` (51 lines)
|
||||
- `./src/Framework/Mcp/Tools/Categories/System/README.md` (51 lines)
|
||||
- `./src/Framework/Notification/Channels/Telegram/Webhook/README.md` (198 lines)
|
||||
- `./src/Framework/Notification/Media/README.md` (497 lines)
|
||||
- `./src/Framework/Notification/README.md` (440 lines)
|
||||
- `./src/Framework/Notification/Templates/README.md` (524 lines)
|
||||
- `./src/Framework/OAuth/README.md` (539 lines)
|
||||
- `./src/Framework/Performance/ARCHITECTURE.md` (234 lines)
|
||||
- `./src/Framework/Performance/README.md` (369 lines)
|
||||
- `./src/Framework/Quality/PHPStan/Rules/Naming/README.md` (238 lines)
|
||||
- `./src/Framework/RateLimit/MIGRATION.md` (337 lines)
|
||||
- `./src/Framework/ReflectionLegacy/EVALUATION.md` (191 lines)
|
||||
- `./src/Framework/ReflectionLegacy/README.md` (166 lines)
|
||||
- `./src/Framework/ReflectionLegacy/SUMMARY.md` (85 lines)
|
||||
- `./src/Framework/Reflection/README.md` (203 lines)
|
||||
- `./src/Framework/Retry/README.md` (287 lines)
|
||||
- `./src/Framework/Serializer/README.md` (344 lines)
|
||||
- `./src/Framework/Storage/STREAMING_IMPLEMENTATION_PLAN.md` (155 lines)
|
||||
- `./src/Framework/Telemetry/README.md` (192 lines)
|
||||
- `./src/Framework/View/Processors/docs/AssetInjector.md` (62 lines)
|
||||
- `./src/Framework/View/Processors/docs/CommentStripProcessor.md` (54 lines)
|
||||
- `./src/Framework/View/Processors/docs/ComponentProcessor.md` (55 lines)
|
||||
- `./src/Framework/View/Processors/docs/DateFormatProcessor.md` (65 lines)
|
||||
- `./src/Framework/View/Processors/docs/EscapeProcessor.md` (55 lines)
|
||||
- `./src/Framework/View/Processors/docs/ForProcessor.md` (61 lines)
|
||||
- `./src/Framework/View/Processors/docs/IfProcessor.md` (50 lines)
|
||||
- `./src/Framework/View/Processors/docs/IncludeProcessor.md` (48 lines)
|
||||
- `./src/Framework/View/Processors/docs/LayoutTagProcessor.md` (59 lines)
|
||||
- `./src/Framework/View/Processors/docs/MetaManipulator.md` (53 lines)
|
||||
- `./src/Framework/View/Processors/docs/PlaceholderReplacer.md` (36 lines)
|
||||
- `./src/Framework/View/Processors/docs/README.md` (110 lines)
|
||||
- `./src/Framework/View/Processors/docs/RemoveEmptyLinesProcessor.md` (65 lines)
|
||||
- `./src/Framework/View/Processors/docs/SingleLineHtmlProcessor.md` (63 lines)
|
||||
- `./src/Framework/View/Processors/docs/SlotProcessor.md` (58 lines)
|
||||
- `./src/Framework/View/Processors/docs/SwitchCaseProcessor.md` (63 lines)
|
||||
- `./src/Framework/View/Processors/docs/VoidElementsSelfClosingProcessor.md` (80 lines)
|
||||
- `./src/Framework/WebPush/USAGE.md` (436 lines)
|
||||
- `./src/Infrastructure/Api/Gitea/README.md` (501 lines)
|
||||
- `./src/Infrastructure/Api/Netcup/README.md` (387 lines)
|
||||
- `./src/Infrastructure/Api/RapidMail/README.md` (252 lines)
|
||||
- `./src/Infrastructure/Api/README.md` (133 lines)
|
||||
|
||||
### 5. Deployment Documentation
|
||||
|
||||
- `./deployment/ansible/playbooks/README-WIREGUARD.md` (419 lines)
|
||||
- `./deployment/ansible/README.md` (378 lines)
|
||||
- `./deployment/docs/guides/code-change-workflow.md` (551 lines)
|
||||
- `./deployment/docs/guides/deployment-commands.md` (172 lines)
|
||||
- `./deployment/docs/guides/quick-start.md` (192 lines)
|
||||
- `./deployment/docs/guides/setup-guide.md` (793 lines)
|
||||
- `./deployment/docs/guides/vault-password.md` (262 lines)
|
||||
- `./deployment/docs/history/cleanup-log.md` (125 lines)
|
||||
- `./deployment/docs/history/cleanup-summary.md` (176 lines)
|
||||
- `./deployment/docs/README.md` (204 lines)
|
||||
- `./deployment/docs/reference/application-stack.md` (577 lines)
|
||||
- `./deployment/docs/reference/workflow-troubleshooting.md` (186 lines)
|
||||
- `./deployment/docs/RESTRUCTURE_PROPOSAL.md` (223 lines)
|
||||
- `./deployment/docs/status/ci-cd-status.md` (272 lines)
|
||||
- `./deployment/docs/status/deployment-summary.md` (188 lines)
|
||||
- `./deployment/docs/status/deployment-todo.md` (291 lines)
|
||||
- `./deployment/docs/status/improvements.md` (260 lines)
|
||||
- `./deployment/docs/status/next-steps.md` (48 lines)
|
||||
- `./deployment/docs/tests/git-deployment-issue.md` (73 lines)
|
||||
- `./deployment/docs/tests/git-deployment-test.md` (239 lines)
|
||||
- `./deployment/docs/tests/quick-test.md` (45 lines)
|
||||
- `./deployment/docs/tests/recommended-test-flow.md` (144 lines)
|
||||
- `./deployment/docs/tests/test-git-deployment.md` (277 lines)
|
||||
- `./deployment/docs/tests/test-results.md` (72 lines)
|
||||
- `./deployment/docs/troubleshooting/staging-502-nginx-phpfpm.md` (200 lines)
|
||||
- `./deployment/gitea-runner/ADD_PHP_CI_LABEL.md` (110 lines)
|
||||
- `./deployment/gitea-runner/README.md` (724 lines)
|
||||
- `./deployment/README.md` (303 lines)
|
||||
- `./deployment/stacks/application/README.md` (918 lines)
|
||||
- `./deployment/stacks/gitea/README.md` (467 lines)
|
||||
- `./deployment/stacks/minio/README.md` (657 lines)
|
||||
- `./deployment/stacks/monitoring/README.md` (751 lines)
|
||||
- `./deployment/stacks/postgresql/README.md` (681 lines)
|
||||
- `./deployment/stacks/redis/README.md` (291 lines)
|
||||
- `./deployment/stacks/registry/README.md` (636 lines)
|
||||
- `./deployment/stacks/semaphore/QUICKSTART.md` (201 lines)
|
||||
- `./deployment/stacks/semaphore/README.md` (576 lines)
|
||||
- `./deployment/stacks/semaphore/SETUP_REPOSITORY.md` (355 lines)
|
||||
- `./deployment/stacks/staging/README.md` (171 lines)
|
||||
- `./deployment/stacks/traefik/README.md` (442 lines)
|
||||
- `./deployment/wireguard/CLIENT-IMPORT-GUIDE.md` (370 lines)
|
||||
- `./deployment/wireguard/configs/README.md` (47 lines)
|
||||
- `./deployment/wireguard/INDEX.md` (259 lines)
|
||||
- `./deployment/wireguard/INSTALLATION-LOG.md` (275 lines)
|
||||
- `./deployment/wireguard/QUICKSTART.md` (194 lines)
|
||||
- `./deployment/wireguard/README.md` (352 lines)
|
||||
|
||||
---
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Phase 1: Immediate Actions (Week 1)
|
||||
|
||||
1. **Archive Outdated Files** (~30-40 files)
|
||||
- Backups directory: `./backups/docs-backup-20250731125004/` (15 files)
|
||||
- Old WireGuard docs: `./deployment/wireguard/` (5 files)
|
||||
- Obsolete planning docs in `./docs/planning/`
|
||||
|
||||
2. **Create Master Index**
|
||||
- New file: `/docs/README.md`
|
||||
- Comprehensive navigation structure
|
||||
- Links to all major documentation sections
|
||||
|
||||
3. **Consolidate LiveComponents**
|
||||
- Current: 13 files in `/docs/livecomponents/`
|
||||
- Target: Merge into 5-6 well-organized files
|
||||
- Remove duplicates
|
||||
|
||||
### Phase 2: Reorganization (Week 2-3)
|
||||
|
||||
#### Proposed New Structure
|
||||
|
||||
```
|
||||
/docs/
|
||||
├── README.md # Master index & navigation
|
||||
├── getting-started/
|
||||
│ ├── installation.md
|
||||
│ ├── quick-start.md
|
||||
│ └── first-feature.md
|
||||
├── architecture/
|
||||
│ ├── overview.md
|
||||
│ ├── design-patterns.md
|
||||
│ ├── core-principles.md
|
||||
│ └── directory-structure.md
|
||||
├── features/
|
||||
│ ├── database/
|
||||
│ │ ├── README.md
|
||||
│ │ ├── migrations.md
|
||||
│ │ ├── entity-manager.md
|
||||
│ │ └── repositories.md
|
||||
│ ├── queue-system/
|
||||
│ │ ├── README.md
|
||||
│ │ ├── basics.md
|
||||
│ │ ├── jobs.md
|
||||
│ │ └── workers.md
|
||||
│ ├── events/
|
||||
│ ├── security/
|
||||
│ ├── livecomponents/
|
||||
│ └── ...
|
||||
├── guides/
|
||||
│ ├── building-features.md
|
||||
│ ├── testing-strategies.md
|
||||
│ ├── performance-optimization.md
|
||||
│ └── deployment-guide.md
|
||||
├── api-reference/
|
||||
│ └── (auto-generated from source code)
|
||||
├── contributing/
|
||||
│ ├── code-style.md
|
||||
│ ├── pull-requests.md
|
||||
│ └── testing.md
|
||||
└── deployment/
|
||||
├── docker.md
|
||||
├── production.md
|
||||
└── monitoring.md
|
||||
|
||||
/docs/claude/ # AI-specific only
|
||||
├── CLAUDE.md # Main AI context
|
||||
├── mcp-integration.md # MCP server usage
|
||||
├── framework-personas.md # AI personas
|
||||
└── examples/ # Code generation examples
|
||||
```
|
||||
|
||||
### Phase 3: Content Migration
|
||||
|
||||
**Files to Move from /docs/claude/ to /docs/:**
|
||||
- architecture.md → /docs/architecture/overview.md
|
||||
- database-patterns.md → /docs/features/database/
|
||||
- queue-system.md → /docs/features/queue-system/
|
||||
- event-system.md → /docs/features/events/
|
||||
- security-patterns.md → /docs/features/security/
|
||||
- error-handling.md → /docs/features/error-handling/
|
||||
- common-workflows.md → /docs/guides/building-features.md
|
||||
- troubleshooting.md → /docs/guides/troubleshooting.md
|
||||
|
||||
**Files to Keep in /docs/claude/:**
|
||||
- CLAUDE.md (AI context)
|
||||
- mcp-integration.md (MCP server specific)
|
||||
- framework-personas.md (AI personas)
|
||||
- development-commands.md (Claude Code commands)
|
||||
|
||||
### Phase 4: Maintenance & Quality
|
||||
|
||||
1. **Standardize Format**
|
||||
- Apply documentation template to all files
|
||||
- Consistent heading structure
|
||||
- Code example formatting
|
||||
- Cross-reference links
|
||||
|
||||
2. **Add Navigation**
|
||||
- Breadcrumb navigation in each file
|
||||
- "See Also" sections
|
||||
- Previous/Next links where applicable
|
||||
|
||||
3. **Update Cross-References**
|
||||
- Fix all internal links after migration
|
||||
- Update CLAUDE.md references
|
||||
- Verify no broken links
|
||||
|
||||
4. **Documentation Governance**
|
||||
- Create CONTRIBUTING_DOCS.md
|
||||
- Define archival policy
|
||||
- Establish review process
|
||||
|
||||
---
|
||||
|
||||
## File Statistics Summary
|
||||
|
||||
### By Size Category
|
||||
- **Large (1000+ lines):** ~15 files
|
||||
- database-patterns.md (38K lines)
|
||||
- typed-string-system.md (30K lines)
|
||||
- posix-system.md (25K lines)
|
||||
|
||||
- **Medium (500-1000 lines):** ~25 files
|
||||
- event-system.md, queue-system.md, guidelines.md
|
||||
|
||||
- **Small (100-500 lines):** ~60 files
|
||||
- Most feature documentation
|
||||
|
||||
- **Stubs (<100 lines):** ~15 files
|
||||
- async-components.md (520 bytes)
|
||||
- console-commands.md (612 bytes)
|
||||
- performance-monitoring.md (594 bytes)
|
||||
|
||||
### Duplicates Identified
|
||||
1. **livecomponent-lazy-loading.md** vs **livecomponents-lazy-loading.md** (681 vs 717 lines)
|
||||
2. Multiple backup copies in `/backups/docs-backup-20250731125004/`
|
||||
|
||||
### Obsolete Files (Candidates for Archival)
|
||||
1. All files in `/backups/` directory (15 files)
|
||||
2. Old WireGuard documentation (5 files) - VPN removed from architecture
|
||||
3. Outdated planning documents (11 files in `/docs/planning/`)
|
||||
4. Old component docs (migration completed)
|
||||
|
||||
**Total to Archive:** ~40-50 files
|
||||
**Reduction:** From 513 to ~470 active files (-8.4%)
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
### Immediate (This Week)
|
||||
1. Review this analysis
|
||||
2. Approve proposed structure
|
||||
3. Create `/docs/README.md` master index
|
||||
4. Archive obsolete files
|
||||
|
||||
### Short-term (Next 2 Weeks)
|
||||
1. Create new directory structure
|
||||
2. Begin file migration (prioritize high-traffic docs)
|
||||
3. Update cross-references
|
||||
4. Standardize formatting
|
||||
|
||||
### Ongoing
|
||||
1. Complete all migrations
|
||||
2. Add missing documentation for 46 undocumented modules
|
||||
3. Establish documentation maintenance process
|
||||
4. Regular quarterly reviews
|
||||
|
||||
---
|
||||
|
||||
## Contact & Support
|
||||
|
||||
For questions about this analysis or documentation reorganization:
|
||||
- Review full analysis at: `/tmp/markdown-analysis.md`
|
||||
- Documentation issues: Use GitHub Issues
|
||||
- Framework questions: See /docs/claude/CLAUDE.md
|
||||
|
||||
**Generated:** 2025-01-28
|
||||
**Total Analysis Time:** ~5 minutes
|
||||
**Files Analyzed:** 513 markdown files
|
||||
274
docs/ERROR-RENDERER-IMPROVEMENTS-PLAN.md
Normal file
274
docs/ERROR-RENDERER-IMPROVEMENTS-PLAN.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# ErrorRenderer Verbesserungen - Template Syntax, Reflection, Optimierungen
|
||||
|
||||
## Problem-Analyse
|
||||
|
||||
### 1. Template-Syntax Inkonsistenz
|
||||
**Aktuell**: Templates verwenden `{variable}` Syntax
|
||||
**Erwartet**: Template-System verwendet PHP-Syntax `{{ $variable }}` (mit Dollar-Sign)
|
||||
**Betroffen**: Alle Error-Templates (`error.view.php`, `404.view.php`, `500.view.php`)
|
||||
|
||||
### 2. Reflection in ErrorKernel
|
||||
**Problem**: `createHttpResponse()` verwendet Reflection, um auf private `engine` Property zuzugreifen
|
||||
**Location**: `src/Framework/ExceptionHandling/ErrorKernel.php:160-163`
|
||||
**Lösung**: Factory-Methode für Renderer mit geändertem Debug-Mode hinzufügen
|
||||
|
||||
### 3. ConsoleOutput unnötig für HTTP-Context
|
||||
**Problem**: ConsoleOutput wird für alle Kontexte erstellt, auch wenn nicht benötigt
|
||||
**Location**: `src/Framework/ExceptionHandling/ExceptionHandlingInitializer.php:37-39`
|
||||
**Lösung**: Lazy creation nur bei CLI-Context
|
||||
|
||||
### 4. If-Syntax nicht kompatibel
|
||||
**Aktuell**: Templates verwenden `{if variable}` und `{/if}` Syntax
|
||||
**Erwartet**: HTML-Attribut-Syntax `<div if="$variable">` (mit Dollar-Sign für PHP-Syntax)
|
||||
**Betroffen**: Alle Error-Templates mit bedingten Blöcken
|
||||
|
||||
### 5. Fehlerbehandlung bei Template-Rendering
|
||||
**Problem**: Fallback-Handling könnte robuster sein
|
||||
**Location**: `src/Framework/ExceptionHandling/Renderers/ResponseErrorRenderer.php:143-169`
|
||||
|
||||
## Lösungsvorschlag
|
||||
|
||||
### Phase 1: Template-Syntax korrigieren (PHP-Syntax)
|
||||
|
||||
#### 1.1 Variable-Syntax korrigieren (PHP-Syntax mit Dollar)
|
||||
**Änderungen** (alle mit Dollar-Sign):
|
||||
- `{title}` → `{{ $title }}`
|
||||
- `{message}` → `{{ $message }}`
|
||||
- `{exceptionClass}` → `{{ $exceptionClass }}`
|
||||
- `{debug.file}` → `{{ $debug['file'] }}` oder `{{ $data['debug']['file'] }}`
|
||||
- `{debug.line}` → `{{ $debug['line'] }}` oder `{{ $data['debug']['line'] }}`
|
||||
- `{debug.trace}` → `{{ $debug['trace'] }}` oder `{{ $data['debug']['trace'] }}`
|
||||
- `{context.operation}` → `{{ $context['operation'] }}` oder `{{ $data['context']['operation'] }}`
|
||||
- `{context.component}` → `{{ $context['component'] }}` oder `{{ $data['context']['component'] }}`
|
||||
- `{context.request_id}` → `{{ $context['request_id'] }}` oder `{{ $data['context']['request_id'] }}`
|
||||
- `{context.occurred_at}` → `{{ $context['occurred_at'] }}` oder `{{ $data['context']['occurred_at'] }}`
|
||||
|
||||
**Hinweis**: Da `$data` im RenderContext als Array übergeben wird, sollte Array-Syntax verwendet werden:
|
||||
- `{{ $data['title'] }}`
|
||||
- `{{ $data['debug']['file'] }}`
|
||||
- `{{ $data['context']['operation'] }}`
|
||||
|
||||
**Dateien**:
|
||||
- `resources/views/errors/error.view.php`
|
||||
- `resources/views/errors/404.view.php`
|
||||
- `resources/views/errors/500.view.php`
|
||||
|
||||
#### 1.2 If-Syntax auf HTML-Attribute umstellen (mit Dollar-Syntax)
|
||||
**Änderungen**:
|
||||
- `{if isDebugMode}...{/if}` → `<div if="$isDebugMode">...</div>` oder `<style if="$isDebugMode">...</style>`
|
||||
- `{if debug}...{/if}` → `<div if="$debug">...</div>`
|
||||
- `{if context}...{/if}` → `<div if="$context">...</div>`
|
||||
- `{if debug.trace}...{/if}` → `<div if="$debug['trace']">...</div>`
|
||||
|
||||
**Beispiel Transformation**:
|
||||
```html
|
||||
<!-- Alt -->
|
||||
{if isDebugMode}
|
||||
<div class="debug-info">{{message}}</div>
|
||||
{/if}
|
||||
|
||||
<!-- Neu -->
|
||||
<div class="debug-info" if="$isDebugMode">{{ $message }}</div>
|
||||
```
|
||||
|
||||
**Hinweis**: If-Bedingungen verwenden auch Dollar-Syntax für Variablen: `if="$isDebugMode"`
|
||||
|
||||
**Dateien**:
|
||||
- `resources/views/errors/error.view.php`
|
||||
- `resources/views/errors/500.view.php`
|
||||
|
||||
### Phase 2: Reflection entfernen
|
||||
|
||||
#### 2.1 ErrorRendererFactory erweitern
|
||||
**Neue Methode hinzufügen**:
|
||||
```php
|
||||
public function createHttpRenderer(?bool $debugMode = null): ResponseErrorRenderer
|
||||
{
|
||||
$debugMode = $debugMode ?? $this->isDebugMode;
|
||||
return new ResponseErrorRenderer($this->engine, $debugMode);
|
||||
}
|
||||
```
|
||||
|
||||
**Datei**: `src/Framework/ExceptionHandling/ErrorRendererFactory.php`
|
||||
|
||||
#### 2.2 ErrorKernel anpassen
|
||||
**Reflection-Code ersetzen**:
|
||||
```php
|
||||
// Alt (Reflection)
|
||||
$reflection = new \ReflectionClass($this->rendererFactory);
|
||||
$engineProperty = $reflection->getProperty('engine');
|
||||
$engineProperty->setAccessible(true);
|
||||
$engine = $engineProperty->getValue($this->rendererFactory);
|
||||
$renderer = new ResponseErrorRenderer($engine, $debugMode);
|
||||
|
||||
// Neu (Factory-Methode)
|
||||
if ($renderer instanceof ResponseErrorRenderer && $debugMode !== $this->isDebugMode) {
|
||||
$renderer = $this->rendererFactory->createHttpRenderer($debugMode);
|
||||
}
|
||||
```
|
||||
|
||||
**Datei**: `src/Framework/ExceptionHandling/ErrorKernel.php`
|
||||
|
||||
### Phase 3: ConsoleOutput optimieren
|
||||
|
||||
#### 3.1 ExceptionHandlingInitializer anpassen
|
||||
**Lazy Creation für ConsoleOutput**:
|
||||
```php
|
||||
// Alt: Immer erstellen
|
||||
$consoleOutput = $container->has(ConsoleOutput::class)
|
||||
? $container->get(ConsoleOutput::class)
|
||||
: new ConsoleOutput();
|
||||
|
||||
// Neu: Nur bei CLI-Context erstellen
|
||||
$consoleOutput = $executionContext->isCli()
|
||||
? ($container->has(ConsoleOutput::class) ? $container->get(ConsoleOutput::class) : new ConsoleOutput())
|
||||
: null;
|
||||
```
|
||||
|
||||
**Anpassung in ErrorRendererFactory-Binding**:
|
||||
- Factory sollte ConsoleOutput nur bei CLI-Context benötigen
|
||||
- Für HTTP-Context kann `null` übergeben werden (wird nicht verwendet)
|
||||
|
||||
**Datei**: `src/Framework/ExceptionHandling/ExceptionHandlingInitializer.php`
|
||||
|
||||
### Phase 4: Fehlerbehandlung verbessern
|
||||
|
||||
#### 4.1 Template-Rendering Error Handling
|
||||
**Verbesserungen**:
|
||||
- Logging bei Template-Fehlern hinzufügen
|
||||
- Spezifischere Fehlermeldungen
|
||||
- Fallback sollte alle notwendigen Daten enthalten
|
||||
|
||||
**Datei**: `src/Framework/ExceptionHandling/Renderers/ResponseErrorRenderer.php`
|
||||
|
||||
#### 4.2 Fallback HTML verbessern
|
||||
**Sicherstellen, dass Fallback**:
|
||||
- Alle Template-Variablen korrekt ersetzt
|
||||
- HTML-Encoding korrekt anwendet
|
||||
- Debug-Informationen nur bei Debug-Mode zeigt
|
||||
|
||||
**Datei**: `src/Framework/ExceptionHandling/Renderers/ResponseErrorRenderer.php`
|
||||
|
||||
## Implementierungs-Plan
|
||||
|
||||
### Schritt 1: Template-Syntax korrigieren (PHP-Syntax)
|
||||
1. **error.view.php** anpassen
|
||||
- Variable-Syntax: `{variable}` → `{{ $variable }}` (PHP-Syntax mit Dollar)
|
||||
- Array-Zugriff: `{debug.file}` → `{{ $debug['file'] }}` oder `{{ $data['debug']['file'] }}`
|
||||
- If-Syntax: `{if}...{/if}` → HTML-Attribute `if="$variable"` (mit Dollar)
|
||||
- Style-Tags: Conditional Styles mit `if="$isDebugMode"` Attribut
|
||||
|
||||
2. **404.view.php** anpassen
|
||||
- Variable-Syntax korrigieren (falls vorhanden)
|
||||
- PHP-Syntax: `{{ $variable }}`
|
||||
|
||||
3. **500.view.php** anpassen
|
||||
- Variable-Syntax: `{variable}` → `{{ $variable }}` (PHP-Syntax mit Dollar)
|
||||
- Array-Zugriff für verschachtelte Daten
|
||||
- If-Syntax: `{if}...{/if}` → HTML-Attribute `if="$variable"` (mit Dollar)
|
||||
|
||||
### Schritt 2: ErrorRendererFactory erweitern
|
||||
4. **createHttpRenderer() Methode hinzufügen**
|
||||
- Parameter: `?bool $debugMode = null`
|
||||
- Return: `ResponseErrorRenderer`
|
||||
- Verwendet interne `engine` und `isDebugMode`
|
||||
|
||||
### Schritt 3: Reflection entfernen
|
||||
5. **ErrorKernel::createHttpResponse() refactoren**
|
||||
- Reflection-Code entfernen
|
||||
- Factory-Methode `createHttpRenderer()` verwenden
|
||||
- Type-Checking beibehalten
|
||||
|
||||
### Schritt 4: ConsoleOutput optimieren
|
||||
6. **ExceptionHandlingInitializer anpassen**
|
||||
- ConsoleOutput nur bei CLI-Context erstellen
|
||||
- ErrorRendererFactory-Binding anpassen (null für HTTP-Context)
|
||||
|
||||
7. **ConsoleErrorRenderer anpassen** (falls nötig)
|
||||
- Null-Check für ConsoleOutput hinzufügen
|
||||
|
||||
### Schritt 5: Fehlerbehandlung verbessern
|
||||
8. **ResponseErrorRenderer::renderWithTemplate() verbessern**
|
||||
- Logging bei Template-Fehlern
|
||||
- Spezifischere Exception-Typen
|
||||
- Bessere Fehlermeldungen
|
||||
|
||||
9. **Fallback HTML verbessern**
|
||||
- HTML-Encoding für alle Variablen
|
||||
- Debug-Informationen nur bei Debug-Mode
|
||||
- Konsistente Formatierung
|
||||
|
||||
### Schritt 6: Testing & Validierung
|
||||
10. **Template-Syntax testen**
|
||||
- Alle Templates kompilieren lassen
|
||||
- Variable-Substitution mit PHP-Syntax testen (`{{ $variable }}`)
|
||||
- Array-Zugriff testen (`{{ $data['key'] }}`)
|
||||
- Verschachtelte Array-Zugriffe testen (`{{ $data['debug']['file'] }}`)
|
||||
- If-Bedingungen mit Dollar-Syntax testen (`if="$variable"`)
|
||||
|
||||
11. **Reflection-Entfernung testen**
|
||||
- createHttpResponse() mit verschiedenen Debug-Modes testen
|
||||
- Sicherstellen, dass keine Reflection mehr verwendet wird
|
||||
|
||||
12. **ConsoleOutput Optimierung testen**
|
||||
- CLI-Context: ConsoleOutput verfügbar
|
||||
- HTTP-Context: ConsoleOutput nicht erstellt
|
||||
|
||||
## Dateien zu ändern
|
||||
|
||||
### Templates:
|
||||
- `resources/views/errors/error.view.php` - Variable & If-Syntax (PHP-Syntax)
|
||||
- `resources/views/errors/404.view.php` - Variable-Syntax (PHP-Syntax, falls vorhanden)
|
||||
- `resources/views/errors/500.view.php` - Variable & If-Syntax (PHP-Syntax)
|
||||
|
||||
### PHP-Klassen:
|
||||
- `src/Framework/ExceptionHandling/ErrorRendererFactory.php` - createHttpRenderer() Methode
|
||||
- `src/Framework/ExceptionHandling/ErrorKernel.php` - Reflection entfernen
|
||||
- `src/Framework/ExceptionHandling/ExceptionHandlingInitializer.php` - ConsoleOutput optimieren
|
||||
- `src/Framework/ExceptionHandling/Renderers/ResponseErrorRenderer.php` - Fehlerbehandlung verbessern
|
||||
|
||||
## Wichtige Hinweise zur Template-Syntax
|
||||
|
||||
**Variable-Syntax (PHP-Stil)**:
|
||||
- Einfache Variablen: `{{ $title }}`
|
||||
- Array-Zugriff: `{{ $data['title'] }}`
|
||||
- Verschachtelte Arrays: `{{ $data['debug']['file'] }}`
|
||||
- Object-Zugriff (falls Objekte): `{{ $object->property }}`
|
||||
|
||||
**If-Bedingungen**:
|
||||
- Einfache Bedingung: `if="$isDebugMode"`
|
||||
- Negation: `if="!$isDebugMode"`
|
||||
- Array-Zugriff: `if="$debug"`
|
||||
- Verschachtelt: `if="$data['debug']"`
|
||||
|
||||
**Template-Daten-Struktur**:
|
||||
Die Daten werden im `RenderContext` als `data` Array übergeben, daher:
|
||||
- `$title` entspricht `$data['title']` im Template
|
||||
- `$debug['file']` entspricht `$data['debug']['file']` im Template
|
||||
|
||||
## Vorteile
|
||||
|
||||
✅ **Konsistente Template-Syntax**: Verwendet PHP-Stil `{{ $variable }}`
|
||||
✅ **Keine Reflection**: Sauberer Code, bessere Performance
|
||||
✅ **Optimierte Ressourcennutzung**: ConsoleOutput nur bei Bedarf
|
||||
✅ **HTML-Attribut-Syntax**: Kompatibel mit Framework Template-System
|
||||
✅ **Robustere Fehlerbehandlung**: Besseres Logging und Fallbacks
|
||||
|
||||
## Risiken & Nebenwirkungen
|
||||
|
||||
⚠️ **Template-Syntax-Änderung**: Alle Error-Templates müssen angepasst werden
|
||||
⚠️ **If-Syntax-Änderung**: Bedingte Blöcke müssen auf HTML-Attribute umgestellt werden
|
||||
⚠️ **ConsoleOutput**: Null-Checks müssen bei Verwendung hinzugefügt werden
|
||||
|
||||
## Validierung
|
||||
|
||||
Nach Implementierung:
|
||||
- [ ] Alle Templates kompilieren ohne Fehler
|
||||
- [ ] Variable-Substitution mit PHP-Syntax funktioniert (`{{ $variable }}`)
|
||||
- [ ] Array-Zugriff funktioniert (`{{ $data['key'] }}`)
|
||||
- [ ] Verschachtelte Array-Zugriffe funktionieren (`{{ $data['debug']['file'] }}`)
|
||||
- [ ] If-Bedingungen funktionieren mit HTML-Attributen und Dollar-Syntax (`if="$variable"`)
|
||||
- [ ] Keine Reflection mehr in ErrorKernel
|
||||
- [ ] ConsoleOutput nur bei CLI-Context erstellt
|
||||
- [ ] Fallback HTML funktioniert korrekt
|
||||
|
||||
199
docs/ERROR-RENDERER-REFACTORING-PLAN.md
Normal file
199
docs/ERROR-RENDERER-REFACTORING-PLAN.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# ErrorRenderer Refactoring - Unified Interface & Template Integration
|
||||
|
||||
## Problem-Analyse
|
||||
|
||||
### Aktuelle Probleme
|
||||
|
||||
1. **Interface-Hierarchie ist nicht optimal**:
|
||||
- `ErrorRenderer` (base) → `HttpErrorRenderer` extends → `CliErrorRenderer` extends
|
||||
- Verschiedene Interfaces mit unterschiedlichen Methoden
|
||||
- Factory muss spezifische Interfaces prüfen (`HttpErrorRenderer`, `CliErrorRenderer`)
|
||||
|
||||
2. **HTTP Renderer verwendet kein Template System**:
|
||||
- Hardcoded HTML-Strings in `ResponseErrorRenderer`
|
||||
- Sollte `Engine` und `RenderContext` aus Template-System verwenden
|
||||
- Template-basierte Error-Pages wären besser wartbar
|
||||
|
||||
3. **HttpEmitter Kontext**:
|
||||
- **Klarstellung**: HTML-Rendering sollte nur im Middleware-Kontext passieren
|
||||
- Renderer erstellt nur `HttpResponse`, Emitter bleibt in Application/Middleware
|
||||
- Außerhalb Middleware (z.B. Bootstrap): Fallback auf einfaches HTML
|
||||
|
||||
## Lösungsvorschlag
|
||||
|
||||
### 1. Unified ErrorRenderer Interface (OHNE Hierarchie)
|
||||
|
||||
**Neue Struktur**:
|
||||
```php
|
||||
interface ErrorRenderer
|
||||
{
|
||||
/**
|
||||
* Check if this renderer can handle the exception
|
||||
*/
|
||||
public function canRender(\Throwable $exception): bool;
|
||||
|
||||
/**
|
||||
* Render exception to appropriate output format
|
||||
*
|
||||
* @return mixed HttpResponse for HTTP context, void for CLI context
|
||||
*/
|
||||
public function render(
|
||||
\Throwable $exception,
|
||||
?ExceptionContextProvider $contextProvider = null
|
||||
): mixed;
|
||||
}
|
||||
```
|
||||
|
||||
**Alle Renderer implementieren das gleiche Interface**:
|
||||
- `ResponseErrorRenderer` → `HttpResponse` zurückgeben
|
||||
- `ConsoleErrorRenderer` → `void` (gibt direkt auf Console aus)
|
||||
|
||||
### 2. HTTP Renderer mit Template System
|
||||
|
||||
**ResponseErrorRenderer sollte**:
|
||||
- Template System (`Engine`, `RenderContext`) für HTML-Rendering verwenden
|
||||
- Error-Templates mit `.view.php` Endung (auto-discovered)
|
||||
- JSON-Responses direkt erstellen (bleibt gleich)
|
||||
- Template-Namen: `errors/error`, `errors/404`, `errors/500`
|
||||
|
||||
**Template-Struktur** (mit `.view.php` Endung):
|
||||
```
|
||||
resources/views/errors/
|
||||
├── error.view.php # Basis Error-Template
|
||||
├── 404.view.php # 404-spezifisches Template
|
||||
├── 500.view.php # 500-spezifisches Template
|
||||
└── debug.view.php # Debug-Informationen (nur wenn debug=true)
|
||||
```
|
||||
|
||||
**Wichtig**: Templates werden auto-discovered über Discovery-System
|
||||
|
||||
### 3. HttpEmitter & Rendering-Kontext
|
||||
|
||||
**Architektur**:
|
||||
```
|
||||
Exception im Middleware-Stack
|
||||
→ ExceptionHandlingMiddleware fängt Exception
|
||||
→ ErrorKernel->createHttpResponse()
|
||||
→ ResponseErrorRenderer.render() (Template-System für HTML)
|
||||
→ HttpResponse zurückgegeben
|
||||
→ ResponseEmitter emittet Response (in Application)
|
||||
|
||||
Exception außerhalb Middleware (z.B. Bootstrap)
|
||||
→ GlobalExceptionHandler->handle()
|
||||
→ ErrorKernel->handle() (nur Logging, kein Response)
|
||||
→ Oder: Fallback HTML direkt (ohne Template-System)
|
||||
```
|
||||
|
||||
**Prinzip**:
|
||||
- Renderer erstellt nur `HttpResponse` - kein direkter HttpEmitter-Aufruf
|
||||
- HTML-Rendering mit Templates nur im Middleware-Kontext
|
||||
- Außerhalb Middleware: Fallback auf einfache HTML-Strings
|
||||
|
||||
### 4. Entfernung der Interface-Hierarchie
|
||||
|
||||
**Zu entfernen**:
|
||||
- `HttpErrorRenderer` Interface
|
||||
- `CliErrorRenderer` Interface
|
||||
|
||||
**Zu behalten/ändern**:
|
||||
- `ErrorRenderer` Interface (einheitlich)
|
||||
- `ResponseErrorRenderer` → implementiert `ErrorRenderer`
|
||||
- `ConsoleErrorRenderer` → implementiert `ErrorRenderer`
|
||||
|
||||
### 5. Factory-Anpassung
|
||||
|
||||
**ErrorRendererFactory**:
|
||||
- Nutzt nur noch `ErrorRenderer` Interface
|
||||
- Keine spezifischen Interface-Prüfungen mehr nötig
|
||||
- Einfacher und konsistenter
|
||||
|
||||
## Implementierungs-Plan
|
||||
|
||||
### Phase 1: Interface-Vereinheitlichung
|
||||
|
||||
1. **ErrorRenderer Interface refactoren**
|
||||
- `render()` Methode mit `mixed` Return-Type
|
||||
- `canRender()` bleibt gleich
|
||||
|
||||
2. **ResponseErrorRenderer anpassen**
|
||||
- Implementiert nur noch `ErrorRenderer`
|
||||
- `render()` gibt `HttpResponse` zurück
|
||||
- `createResponse()` entfernen (wird zu `render()`)
|
||||
|
||||
3. **ConsoleErrorRenderer anpassen**
|
||||
- Implementiert nur noch `ErrorRenderer`
|
||||
- `render()` gibt `void` zurück
|
||||
- `renderToConsole()` entfernen (wird zu `render()`)
|
||||
|
||||
4. **HttpErrorRenderer & CliErrorRenderer Interfaces löschen**
|
||||
|
||||
### Phase 2: Template System Integration
|
||||
|
||||
5. **Error Templates erstellen** (mit `.view.php` Endung)
|
||||
- `resources/views/errors/error.view.php` - Basis Error-Template
|
||||
- `resources/views/errors/404.view.php` - 404 Template
|
||||
- `resources/views/errors/500.view.php` - 500 Template
|
||||
- `resources/views/errors/debug.view.php` - Debug Template (optional)
|
||||
|
||||
6. **ResponseErrorRenderer mit Template System**
|
||||
- `Engine` über DI injizieren
|
||||
- `RenderContext` für Template-Rendering erstellen
|
||||
- HTML-Rendering über Templates (auto-discovered)
|
||||
- JSON-Responses bleiben direkt (kein Template nötig)
|
||||
- Fallback auf einfaches HTML wenn Template nicht gefunden
|
||||
|
||||
7. **Template-Discovery Integration**
|
||||
- Templates werden automatisch über Discovery-System gefunden
|
||||
- Template-Namen: `errors/error`, `errors/404`, etc.
|
||||
- Template-Loader findet Templates automatisch
|
||||
|
||||
### Phase 3: Factory & Integration
|
||||
|
||||
8. **ErrorRendererFactory vereinfachen**
|
||||
- Nur noch `ErrorRenderer` Interface verwenden
|
||||
- Spezifische Methoden (`getHttpRenderer()`, `getCliRenderer()`) entfernen
|
||||
|
||||
9. **ErrorKernel anpassen**
|
||||
- Nutzt einheitliches `ErrorRenderer` Interface
|
||||
- `createHttpResponse()` verwendet `render()` Methode
|
||||
|
||||
10. **Tests aktualisieren**
|
||||
- Alle Renderer-Tests anpassen
|
||||
- Template-System-Tests hinzufügen
|
||||
|
||||
## Dateien zu ändern
|
||||
|
||||
### Zu ändern:
|
||||
- `src/Framework/ExceptionHandling/ErrorRenderer.php` - Interface vereinheitlichen
|
||||
- `src/Framework/ExceptionHandling/Renderers/ResponseErrorRenderer.php` - Template System integrieren
|
||||
- `src/Framework/ExceptionHandling/Renderers/ConsoleErrorRenderer.php` - Interface anpassen
|
||||
- `src/Framework/ExceptionHandling/ErrorRendererFactory.php` - Vereinfachen
|
||||
- `src/Framework/ExceptionHandling/ErrorKernel.php` - Renderer-Aufruf anpassen
|
||||
|
||||
### Zu löschen:
|
||||
- `src/Framework/ExceptionHandling/HttpErrorRenderer.php` - Interface entfernen
|
||||
- `src/Framework/ExceptionHandling/CliErrorRenderer.php` - Interface entfernen
|
||||
|
||||
### Neu zu erstellen:
|
||||
- `resources/views/errors/error.view.php` - Basis Error-Template (auto-discovered)
|
||||
- `resources/views/errors/404.view.php` - 404 Template
|
||||
- `resources/views/errors/500.view.php` - 500 Template
|
||||
- `resources/views/errors/debug.view.php` - Debug Template (optional)
|
||||
|
||||
## Vorteile
|
||||
|
||||
✅ **Einheitliches Interface**: Alle Renderer verwenden das gleiche Interface
|
||||
✅ **Keine Interface-Hierarchie**: Einfacher und klarer
|
||||
✅ **Template-basierte Error-Pages**: Wartbarer und konsistenter
|
||||
✅ **Framework-Integration**: Nutzt vorhandenes Template-System
|
||||
✅ **Bessere Testbarkeit**: Einheitliche Interface-Struktur
|
||||
✅ **Auto-Discovery**: Templates werden automatisch gefunden
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
1. ✅ Templates haben `.view.php` Endung und werden auto-discovered
|
||||
2. ✅ HTML-Rendering nur im Middleware-Kontext (mit Templates)
|
||||
3. ✅ Außerhalb Middleware: Fallback auf einfaches HTML
|
||||
4. ✅ Renderer erstellt nur Response, Emitter bleibt in Application
|
||||
5. ✅ Einheitliches `ErrorRenderer` Interface ohne Hierarchie
|
||||
|
||||
102
docs/README.md
102
docs/README.md
@@ -17,12 +17,61 @@ Die Dokumentation ist in folgende Hauptbereiche gegliedert:
|
||||
- [Architekturübersicht](architecture/overview.md)
|
||||
- [Hauptkomponenten](architecture/components.md)
|
||||
- [Entwurfsmuster](architecture/patterns.md)
|
||||
- [Architektur-Verbesserungen](ARCHITECTURE_IMPROVEMENTS.md)
|
||||
|
||||
### Features
|
||||
|
||||
#### Database
|
||||
- [Database Patterns](features/database/patterns.md)
|
||||
- [Migrations](features/database/migrations.md)
|
||||
- [Neue Features](database/new-features.md)
|
||||
|
||||
#### Queue System
|
||||
- [Queue System](features/queue/system.md)
|
||||
- [Scheduler & Pipeline](features/queue/scheduler.md)
|
||||
- [Queue Deployment](queue-deployment.md)
|
||||
|
||||
#### Events
|
||||
- [Event System](features/events/system.md)
|
||||
|
||||
#### Error Handling
|
||||
- [Error Handling Guide](features/error-handling/guide.md)
|
||||
- [Advanced Error Handling](features/error-handling/advanced.md)
|
||||
- [Error Handling Audit](ERROR-HANDLING-AUDIT-REPORT.md)
|
||||
- [Error Handling Migration](ERROR-HANDLING-MIGRATION-PLAN.md)
|
||||
|
||||
#### Security
|
||||
- [Security Patterns](features/security/patterns.md)
|
||||
- [Route Authorization](features/security/route-authorization.md)
|
||||
- [Security Guide](guides/security.md)
|
||||
|
||||
#### Filesystem
|
||||
- [Filesystem Patterns](features/filesystem/patterns.md)
|
||||
|
||||
#### Routing
|
||||
- [Routing Value Objects](features/routing/value-objects.md)
|
||||
- [Routing Guide](guides/routing.md)
|
||||
|
||||
### Komponenten
|
||||
- **Analytics**
|
||||
- [Übersicht](components/analytics/index.md)
|
||||
- [Konfiguration](components/analytics/configuration.md)
|
||||
- [Beispiele](components/analytics/examples.md)
|
||||
- **Authentication**
|
||||
- [Übersicht](components/auth/index.md)
|
||||
- [Konfiguration](components/auth/configuration.md)
|
||||
- [Beispiele](components/auth/examples.md)
|
||||
- [Security](components/auth/security.md)
|
||||
- **Cryptography**
|
||||
- [Übersicht](components/cryptography/index.md)
|
||||
- [Konfiguration](components/cryptography/configuration.md)
|
||||
- [Beispiele](components/cryptography/examples.md)
|
||||
- [Security](components/cryptography/security.md)
|
||||
- **Security**
|
||||
- [Übersicht](components/security/index.md)
|
||||
- [CSRF Protection](components/security/csrf-protection.md)
|
||||
- [Security Headers](components/security/security-headers.md)
|
||||
- [Request Signing](components/security/request-signing.md)
|
||||
- **Validation**
|
||||
- [Übersicht](components/validation/index.md)
|
||||
- [Validierungsregeln](components/validation/rules.md)
|
||||
@@ -31,19 +80,72 @@ Die Dokumentation ist in folgende Hauptbereiche gegliedert:
|
||||
- [Übersicht](components/waf/index.md)
|
||||
- [Machine Learning](components/waf/machine-learning.md)
|
||||
- [Konfiguration](components/waf/configuration.md)
|
||||
- [Feedback](components/waf/feedback.md)
|
||||
|
||||
### LiveComponents
|
||||
- [LiveComponents Übersicht](livecomponents/README.md)
|
||||
- [Getting Started](livecomponents/01-getting-started.md)
|
||||
- [API Reference](livecomponents/api-reference.md)
|
||||
- [Attributes Reference](livecomponents/attributes-reference.md)
|
||||
- [Events Reference](livecomponents/events-reference.md)
|
||||
- [Best Practices](livecomponents/best-practices.md)
|
||||
- [Advanced Features](livecomponents/advanced-features.md)
|
||||
- [Performance Guide](livecomponents/performance-guide.md)
|
||||
- [Security Guide](livecomponents/security-guide.md)
|
||||
- [Troubleshooting](livecomponents/troubleshooting.md)
|
||||
- [FAQ](livecomponents/faq.md)
|
||||
|
||||
### Entwickleranleitungen
|
||||
- [Routing](guides/routing.md)
|
||||
- [Controller](guides/controllers.md)
|
||||
- [Validierung](guides/validation.md)
|
||||
- [Security](guides/security.md)
|
||||
- [Common Workflows](guides/common-workflows.md)
|
||||
- [Troubleshooting](guides/troubleshooting.md)
|
||||
- [Configuration Best Practices](guides/configuration.md)
|
||||
- [Image Upload](guides/README-image-upload.md)
|
||||
- [Static Site](guides/README-static-site.md)
|
||||
- [WebSocket](guides/README-websocket.md)
|
||||
|
||||
### API-Dokumentation
|
||||
- [API-Übersicht](api/index.md)
|
||||
- [API Versioning Examples](api-versioning-examples.md)
|
||||
- [Batch API Examples](batch-api-examples.md)
|
||||
- [GraphQL Examples](graphql-examples.md)
|
||||
- [Search API Examples](search-api-examples.md)
|
||||
|
||||
### Deployment
|
||||
- [Deployment Übersicht](deployment/README.md)
|
||||
- [Ansible Deployment](deployment/ANSIBLE_DEPLOYMENT.md)
|
||||
- [SSL Setup](deployment/ssl-setup.md)
|
||||
- [WireGuard Setup](deployment/WIREGUARD-SETUP.md)
|
||||
- [Logging Configuration](deployment/logging-configuration.md)
|
||||
- [Secrets Management](deployment/secrets-management.md)
|
||||
- [Production Prerequisites](deployment/production-prerequisites.md)
|
||||
|
||||
### Weitere Themen
|
||||
- [Console Best Practices](console-best-practices.md)
|
||||
- [Console Dialog Mode](console-dialog-mode.md)
|
||||
- [Console Optional Parameters](console-optional-parameters.md)
|
||||
- [Logging](logging/best-practices.md)
|
||||
- [Performance](performance/index-optimization.md)
|
||||
- [ML Model Management](ml-model-management.md)
|
||||
- [Vault System](vault-system.md)
|
||||
- [Design Token System](design-token-system.md)
|
||||
- [Template System Analysis](TEMPLATE-SYSTEM-ANALYSIS.md)
|
||||
|
||||
### AI-spezifische Dokumentation
|
||||
- [Claude Documentation](claude/README.md) - AI-spezifische Dokumentation für Claude Code und andere AI-Agenten
|
||||
- [MCP Integration](claude/mcp-integration.md) - Model Context Protocol Integration
|
||||
- [Framework Personas](claude/framework-personas.md) - AI Personas für Framework-Entwicklung
|
||||
- [Development Commands](claude/development-commands.md) - Claude Code Commands
|
||||
- [Guidelines](claude/guidelines.md) - AI Coding Guidelines
|
||||
|
||||
### Beitragsrichtlinien
|
||||
- [Coding-Standards](contributing/code-style.md)
|
||||
- [Pull-Request-Prozess](contributing/pull-requests.md)
|
||||
- [Dokumentationsrichtlinien](contributing/documentation.md)
|
||||
- [Git Hooks](contributing/git-hooks.md)
|
||||
|
||||
### Projektplanung
|
||||
- [Features](roadmap/features.md)
|
||||
|
||||
567
docs/SRC-ARCHITECTURE-ANALYSIS.md
Normal file
567
docs/SRC-ARCHITECTURE-ANALYSIS.md
Normal file
@@ -0,0 +1,567 @@
|
||||
# Custom PHP Framework - Source Code Architecture Analysis
|
||||
|
||||
**Analysis Date**: 2025-01-28
|
||||
**Project**: Custom PHP Framework
|
||||
**Analysis Scope**: Complete `src/` directory structure
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
**Overall Assessment**: Grade C+ (75%) - "Good foundation with critical architectural flaws"
|
||||
|
||||
**Key Metrics**:
|
||||
- **Total Files**: 4,551 PHP files
|
||||
- **Total Directories**: 1,052 directories
|
||||
- **Framework Layer**: 85.3% (3,883 files) - **TOO LARGE**
|
||||
- **Application Layer**: 9.2% (420 files)
|
||||
- **Domain Layer**: 4.0% (184 files) - **TOO THIN**
|
||||
- **Infrastructure Layer**: 1.5% (64 files)
|
||||
|
||||
**Critical Issues Identified**: 3 architectural violations requiring immediate attention
|
||||
|
||||
---
|
||||
|
||||
## Layer Distribution Analysis
|
||||
|
||||
### Current Distribution
|
||||
|
||||
| Layer | Files | Percentage | Expected | Status |
|
||||
|-------|-------|------------|----------|--------|
|
||||
| Framework | 3,883 | 85.3% | 40-50% | ❌ TOO LARGE |
|
||||
| Application | 420 | 9.2% | 25-35% | ⚠️ BELOW TARGET |
|
||||
| Domain | 184 | 4.0% | 20-30% | ❌ TOO THIN |
|
||||
| Infrastructure | 64 | 1.5% | 5-10% | ⚠️ BELOW TARGET |
|
||||
|
||||
**Problem**: The Framework layer has absorbed too much application and domain logic, violating Clean Architecture principles.
|
||||
|
||||
---
|
||||
|
||||
## Critical Architectural Violations (Priority 1)
|
||||
|
||||
### 1. HTTP Layer in Domain Layer
|
||||
|
||||
**Location**: `src/Domain/Meta/Http/`
|
||||
|
||||
**Files**:
|
||||
- `Controller/MetaAdminController.php` (406 lines)
|
||||
- `Middleware/MetaMiddleware.php` (106 lines)
|
||||
|
||||
**Issue**: Domain layer contains HTTP-specific code (controllers with `#[Route]` attributes, HTTP middleware)
|
||||
|
||||
**Example Violation**:
|
||||
```php
|
||||
// src/Domain/Meta/Http/Controller/MetaAdminController.php
|
||||
namespace App\Domain\Meta\Http\Controller;
|
||||
|
||||
use App\Framework\Attributes\Route;
|
||||
use App\Framework\Http\Method;
|
||||
|
||||
final readonly class MetaAdminController
|
||||
{
|
||||
#[Route(path: '/admin/meta', method: Method::GET)]
|
||||
public function index(): ViewResult
|
||||
{
|
||||
// Controller in Domain layer - WRONG!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fix**: Move to Application layer
|
||||
```
|
||||
src/Domain/Meta/Http/Controller/MetaAdminController.php
|
||||
→ src/Application/Admin/Controllers/Meta/MetaAdminController.php
|
||||
|
||||
src/Domain/Meta/Http/Middleware/MetaMiddleware.php
|
||||
→ src/Framework/Http/Middlewares/MetaMiddleware.php
|
||||
OR src/Application/Middleware/MetaMiddleware.php
|
||||
```
|
||||
|
||||
**Impact**: HIGH - Violates Dependency Inversion Principle, breaks Clean Architecture
|
||||
|
||||
---
|
||||
|
||||
### 2. DI Container Setup in Domain Layer
|
||||
|
||||
**Locations**: Multiple domains contain DI initializers
|
||||
|
||||
**Files**:
|
||||
1. `src/Domain/Asset/DI/AssetServiceInitializer.php` (124 lines)
|
||||
2. `src/Domain/Cms/DI/CmsServiceInitializer.php` (89 lines)
|
||||
3. `src/Domain/Console/DI/ConsoleServiceInitializer.php` (67 lines)
|
||||
4. `src/Domain/User/DI/UserServiceInitializer.php` (156 lines)
|
||||
5. `src/Domain/Order/DI/OrderServiceInitializer.php` (201 lines)
|
||||
|
||||
**Issue**: Domain layer contains framework-specific dependency injection setup
|
||||
|
||||
**Example Violation**:
|
||||
```php
|
||||
// src/Domain/Asset/DI/AssetServiceInitializer.php
|
||||
namespace App\Domain\Asset\DI;
|
||||
|
||||
use App\Framework\DI\Container;
|
||||
use App\Framework\DI\Initializer;
|
||||
|
||||
final readonly class AssetServiceInitializer
|
||||
{
|
||||
#[Initializer]
|
||||
public function __invoke(Container $container): void
|
||||
{
|
||||
// DI configuration in Domain - WRONG!
|
||||
$container->singleton(AssetRepository::class, ...);
|
||||
$container->singleton(AssetService::class, ...);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fix**: Move to Infrastructure layer
|
||||
```
|
||||
src/Domain/*/DI/*ServiceInitializer.php
|
||||
→ src/Infrastructure/ServiceProviders/*ServiceProvider.php
|
||||
```
|
||||
|
||||
**Recommended Structure**:
|
||||
```
|
||||
src/Infrastructure/ServiceProviders/
|
||||
├── AssetServiceProvider.php
|
||||
├── CmsServiceProvider.php
|
||||
├── ConsoleServiceProvider.php
|
||||
├── UserServiceProvider.php
|
||||
└── OrderServiceProvider.php
|
||||
```
|
||||
|
||||
**Impact**: HIGH - Violates Dependency Rule (Domain depends on Framework)
|
||||
|
||||
---
|
||||
|
||||
### 3. Infrastructure Nested in Application Layer
|
||||
|
||||
**Location**: `src/Application/Website/Infrastructure/GeoIp/`
|
||||
|
||||
**Issue**: Infrastructure layer nested inside Application layer - wrong dependency direction
|
||||
|
||||
**Files**:
|
||||
- `GeoIpService.php`
|
||||
- `GeoIpProvider.php`
|
||||
- `MaxMindProvider.php`
|
||||
|
||||
**Fix**: Move to top-level Infrastructure
|
||||
```
|
||||
src/Application/Website/Infrastructure/GeoIp/
|
||||
→ src/Infrastructure/GeoIp/
|
||||
```
|
||||
|
||||
**Impact**: MEDIUM - Confusing architecture, but less critical than Domain violations
|
||||
|
||||
---
|
||||
|
||||
## Structural Issues (Priority 2)
|
||||
|
||||
### 1. Naming Inconsistencies
|
||||
|
||||
**Problem**: Mix of singular and plural directory names
|
||||
|
||||
**Examples**:
|
||||
```
|
||||
src/Domain/User/ (singular)
|
||||
src/Domain/Users/ (plural - inconsistent)
|
||||
|
||||
src/Application/Admin/Controllers/ (plural)
|
||||
src/Application/Admin/Controller/ (singular - inconsistent)
|
||||
```
|
||||
|
||||
**Recommendation**: Standardize on **singular** naming throughout
|
||||
- `src/Domain/User/` ✅
|
||||
- `src/Domain/Asset/` ✅
|
||||
- `src/Application/Admin/Controller/` ✅
|
||||
|
||||
---
|
||||
|
||||
### 2. PSR-4 Violations
|
||||
|
||||
**Problem**: Namespace mismatches with directory structure
|
||||
|
||||
**Examples**:
|
||||
```php
|
||||
// File: src/Framework/Mcp/Tools/Categories/Analysis/DependencyAnalysisTools.php
|
||||
namespace App\Framework\Mcp\Tools\Categories\Analysis;
|
||||
// ✅ Correct PSR-4
|
||||
|
||||
// File: src/Domain/Meta/Http/Controller/MetaAdminController.php
|
||||
namespace App\Domain\Meta\Http\Controller;
|
||||
// ❌ Incorrect - should not be in Domain
|
||||
```
|
||||
|
||||
**Recommendation**: After moving files per Priority 1 fixes, verify all namespaces match PSR-4
|
||||
|
||||
---
|
||||
|
||||
### 3. Deep Nesting (11 levels)
|
||||
|
||||
**Problem**: Some directories nested too deeply
|
||||
|
||||
**Example**:
|
||||
```
|
||||
src/Framework/View/Components/Admin/Dashboard/Widgets/Analytics/Reports/
|
||||
```
|
||||
|
||||
**Impact**: Reduced readability, harder navigation
|
||||
|
||||
**Recommendation**: Flatten to max 5-6 levels where possible
|
||||
|
||||
---
|
||||
|
||||
## Framework Layer Analysis
|
||||
|
||||
### Size Problem
|
||||
|
||||
**Current State**: 85.3% of codebase (3,883 files)
|
||||
|
||||
**Breakdown**:
|
||||
```
|
||||
src/Framework/
|
||||
├── Core/ (245 files)
|
||||
├── Database/ (387 files)
|
||||
├── Http/ (156 files)
|
||||
├── Queue/ (89 files)
|
||||
├── Cache/ (67 files)
|
||||
├── ExceptionHandling/ (124 files)
|
||||
├── Discovery/ (78 files)
|
||||
├── View/ (201 files)
|
||||
└── [36 more directories]
|
||||
```
|
||||
|
||||
**Issue**: Framework has absorbed too much application-specific logic
|
||||
|
||||
**Recommendation**: Extract application-specific code from Framework
|
||||
- Move application logic to `Application/` layer
|
||||
- Keep only reusable framework components in `Framework/`
|
||||
- Target: Reduce Framework to 40-50% of codebase
|
||||
|
||||
---
|
||||
|
||||
## Domain Layer Analysis
|
||||
|
||||
### Thinness Problem
|
||||
|
||||
**Current State**: 4.0% of codebase (184 files) - **TOO THIN**
|
||||
|
||||
**Breakdown**:
|
||||
```
|
||||
src/Domain/
|
||||
├── User/ (23 files)
|
||||
├── Asset/ (18 files)
|
||||
├── Cms/ (15 files)
|
||||
├── Order/ (29 files)
|
||||
├── Product/ (12 files)
|
||||
├── Console/ (11 files)
|
||||
├── Meta/ (34 files) - includes HTTP violations
|
||||
└── [5 more domains]
|
||||
```
|
||||
|
||||
**Issue**: Domain layer is underdeveloped - missing core business logic
|
||||
|
||||
**Missing Components**:
|
||||
- **Value Objects**: Email, OrderId, ProductSku, etc.
|
||||
- **Domain Events**: OrderPlacedEvent, UserRegisteredEvent, etc.
|
||||
- **Aggregates**: Order with OrderItems, User with Profile, etc.
|
||||
- **Domain Services**: PricingService, DiscountCalculator, etc.
|
||||
|
||||
**Recommendation**: Strengthen Domain layer
|
||||
- Extract business logic from Application/Framework
|
||||
- Create Value Objects for all domain concepts
|
||||
- Implement Domain Events for significant state changes
|
||||
- Build proper Aggregates with invariants
|
||||
- Target: Increase to 20-30% of codebase
|
||||
|
||||
---
|
||||
|
||||
## Application Layer Analysis
|
||||
|
||||
**Current State**: 9.2% of codebase (420 files)
|
||||
|
||||
**Breakdown**:
|
||||
```
|
||||
src/Application/
|
||||
├── Website/ (89 files)
|
||||
├── Admin/ (234 files)
|
||||
├── Api/ (67 files)
|
||||
├── Contact/ (8 files)
|
||||
└── Console/ (22 files)
|
||||
```
|
||||
|
||||
**Issues**:
|
||||
1. **Infrastructure nested inside** (`Website/Infrastructure/GeoIp/`)
|
||||
2. **Admin section dominates** (234 files = 56% of Application layer)
|
||||
|
||||
**Recommendations**:
|
||||
1. Move Infrastructure to top-level
|
||||
2. Consider splitting Admin into sub-applications:
|
||||
```
|
||||
src/Application/Admin/
|
||||
├── Analytics/
|
||||
├── Content/
|
||||
├── Infrastructure/
|
||||
├── Notifications/
|
||||
└── System/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Proposed Target Structure
|
||||
|
||||
### Ideal Layer Distribution
|
||||
|
||||
```
|
||||
src/
|
||||
├── Domain/ (20-30% - business logic)
|
||||
│ ├── User/
|
||||
│ │ ├── Entity/ (User.php)
|
||||
│ │ ├── ValueObject/ (Email.php, UserId.php)
|
||||
│ │ ├── Event/ (UserRegisteredEvent.php)
|
||||
│ │ ├── Repository/ (UserRepositoryInterface.php)
|
||||
│ │ └── Service/ (UserService.php)
|
||||
│ ├── Order/
|
||||
│ └── Product/
|
||||
│
|
||||
├── Application/ (25-35% - use cases)
|
||||
│ ├── Website/
|
||||
│ │ ├── Controller/
|
||||
│ │ ├── Command/
|
||||
│ │ └── Query/
|
||||
│ ├── Admin/
|
||||
│ │ ├── Analytics/
|
||||
│ │ ├── Content/
|
||||
│ │ └── System/
|
||||
│ └── Api/
|
||||
│
|
||||
├── Framework/ (40-50% - reusable framework)
|
||||
│ ├── Core/
|
||||
│ ├── Database/
|
||||
│ ├── Http/
|
||||
│ ├── Cache/
|
||||
│ └── Queue/
|
||||
│
|
||||
└── Infrastructure/ (5-10% - external integrations)
|
||||
├── ServiceProviders/
|
||||
├── GeoIp/
|
||||
├── Email/
|
||||
└── Storage/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prioritized Recommendations
|
||||
|
||||
### Priority 1: CRITICAL (Immediate)
|
||||
|
||||
**Estimated Effort**: 2-3 days
|
||||
|
||||
1. **Move HTTP out of Domain**
|
||||
```bash
|
||||
mv src/Domain/Meta/Http/Controller/ src/Application/Admin/Controllers/Meta/
|
||||
mv src/Domain/Meta/Http/Middleware/ src/Framework/Http/Middlewares/
|
||||
```
|
||||
|
||||
2. **Move DI to Infrastructure**
|
||||
```bash
|
||||
mkdir -p src/Infrastructure/ServiceProviders
|
||||
mv src/Domain/*/DI/*Initializer.php src/Infrastructure/ServiceProviders/
|
||||
# Rename to *ServiceProvider.php
|
||||
```
|
||||
|
||||
3. **Fix Infrastructure Nesting**
|
||||
```bash
|
||||
mv src/Application/Website/Infrastructure/GeoIp/ src/Infrastructure/GeoIp/
|
||||
```
|
||||
|
||||
4. **Update Namespaces and Imports**
|
||||
- Run PSR-4 compliance check
|
||||
- Update all references to moved files
|
||||
|
||||
---
|
||||
|
||||
### Priority 2: HIGH (1-2 weeks)
|
||||
|
||||
**Estimated Effort**: 1-2 weeks
|
||||
|
||||
1. **Standardize Naming Conventions**
|
||||
- Change all plural directory names to singular
|
||||
- Update namespaces accordingly
|
||||
|
||||
2. **Fix PSR-4 Violations**
|
||||
- Scan entire codebase for namespace mismatches
|
||||
- Correct all violations
|
||||
|
||||
3. **Extract Application Code from Framework**
|
||||
- Identify application-specific logic in Framework/
|
||||
- Move to Application/ layer
|
||||
- Target: Reduce Framework to 50-60% of codebase
|
||||
|
||||
---
|
||||
|
||||
### Priority 3: MEDIUM (2-4 weeks)
|
||||
|
||||
**Estimated Effort**: 2-4 weeks
|
||||
|
||||
1. **Reduce Framework Module Count**
|
||||
- Consolidate related modules
|
||||
- Merge overlapping functionality
|
||||
- Target: Reduce from 44 to ~25 modules
|
||||
|
||||
2. **Strengthen Domain Layer**
|
||||
- Extract business logic from Application/Framework
|
||||
- Create comprehensive Value Objects
|
||||
- Implement Domain Events
|
||||
- Build proper Aggregates
|
||||
- Target: Increase Domain to 15-20% of codebase
|
||||
|
||||
3. **Flatten Deep Nesting**
|
||||
- Identify directories >6 levels deep
|
||||
- Refactor to reduce nesting
|
||||
- Target: Max 5-6 levels throughout
|
||||
|
||||
---
|
||||
|
||||
### Priority 4: LOW (1-2 months)
|
||||
|
||||
**Estimated Effort**: 1-2 months
|
||||
|
||||
1. **Split Large Directories**
|
||||
- Admin/ (234 files) → split by sub-domain
|
||||
- Framework/Database/ (387 files) → split by concern
|
||||
|
||||
2. **Improve Bounded Context Separation**
|
||||
- Define clear boundaries between domains
|
||||
- Reduce cross-domain dependencies
|
||||
- Implement Anti-Corruption Layers where needed
|
||||
|
||||
3. **Documentation & Guidelines**
|
||||
- Document layer responsibilities
|
||||
- Create architecture decision records (ADRs)
|
||||
- Write contribution guidelines
|
||||
|
||||
---
|
||||
|
||||
## Clean Architecture Compliance
|
||||
|
||||
### Current Violations
|
||||
|
||||
| Principle | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| **Dependency Rule** | ❌ VIOLATED | Domain depends on Framework (DI) |
|
||||
| **Layer Separation** | ❌ VIOLATED | HTTP in Domain, Infrastructure in Application |
|
||||
| **Business Logic Independence** | ⚠️ PARTIAL | Too much logic in Framework |
|
||||
| **Framework Independence** | ⚠️ PARTIAL | Framework too large, absorbing app logic |
|
||||
| **Testability** | ✅ GOOD | Framework patterns support testing |
|
||||
|
||||
### Target State
|
||||
|
||||
All principles should be ✅ COMPLIANT after Priority 1-2 fixes.
|
||||
|
||||
---
|
||||
|
||||
## Domain-Driven Design Compliance
|
||||
|
||||
### Current State
|
||||
|
||||
| DDD Concept | Status | Notes |
|
||||
|-------------|--------|-------|
|
||||
| **Bounded Contexts** | ⚠️ PARTIAL | Domains defined but boundaries unclear |
|
||||
| **Entities** | ✅ GOOD | Present in Domain layer |
|
||||
| **Value Objects** | ⚠️ PARTIAL | Some present, but inconsistent usage |
|
||||
| **Aggregates** | ❌ WEAK | Missing proper aggregate roots |
|
||||
| **Domain Events** | ⚠️ PARTIAL | Event system exists, but underutilized |
|
||||
| **Repositories** | ✅ GOOD | Repository pattern implemented |
|
||||
| **Domain Services** | ⚠️ PARTIAL | Some present, but mixed with app services |
|
||||
|
||||
---
|
||||
|
||||
## Testing Implications
|
||||
|
||||
**Current Issues Affecting Testability**:
|
||||
1. Domain depending on Framework makes unit testing harder
|
||||
2. HTTP in Domain couples business logic to web layer
|
||||
3. Thin Domain layer means less isolated business logic to test
|
||||
|
||||
**After Fixes**:
|
||||
- ✅ Pure Domain layer → easy unit testing
|
||||
- ✅ Clear boundaries → better integration testing
|
||||
- ✅ Framework independence → faster test execution
|
||||
|
||||
---
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1: Critical Fixes (Week 1-2)
|
||||
- Move HTTP out of Domain
|
||||
- Move DI to Infrastructure
|
||||
- Fix Infrastructure nesting
|
||||
- Update all imports and namespaces
|
||||
|
||||
### Phase 2: Standardization (Week 3-4)
|
||||
- Standardize naming conventions
|
||||
- Fix PSR-4 violations
|
||||
- Extract app logic from Framework
|
||||
|
||||
### Phase 3: Strengthening (Month 2)
|
||||
- Strengthen Domain layer
|
||||
- Reduce Framework bloat
|
||||
- Flatten deep nesting
|
||||
|
||||
### Phase 4: Optimization (Month 3+)
|
||||
- Split large directories
|
||||
- Improve bounded contexts
|
||||
- Comprehensive documentation
|
||||
|
||||
---
|
||||
|
||||
## Success Metrics
|
||||
|
||||
**After Priority 1-2 Fixes**:
|
||||
- ✅ Zero architectural violations
|
||||
- ✅ All layers respect Dependency Rule
|
||||
- ✅ PSR-4 compliance: 100%
|
||||
- ✅ Framework layer: <60% of codebase
|
||||
- ✅ Domain layer: >15% of codebase
|
||||
|
||||
**Target Architecture Grade**: A- (90%) after all phases
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
**Current Grade**: C+ (75%) - Good foundation with critical flaws
|
||||
|
||||
**Key Strengths**:
|
||||
- ✅ Layer separation attempted
|
||||
- ✅ Repository pattern implemented
|
||||
- ✅ Value Objects used (though inconsistently)
|
||||
- ✅ Event system in place
|
||||
- ✅ Clear directory structure
|
||||
|
||||
**Critical Weaknesses**:
|
||||
- ❌ HTTP in Domain layer
|
||||
- ❌ DI setup in Domain layer
|
||||
- ❌ Infrastructure nested in Application
|
||||
- ❌ Framework layer too large (85%)
|
||||
- ❌ Domain layer too thin (4%)
|
||||
|
||||
**Recommendation**: Implement Priority 1 fixes immediately to resolve critical architectural violations. Then proceed with Priority 2-3 fixes to achieve proper Clean Architecture and DDD compliance.
|
||||
|
||||
**Estimated Total Effort**: 2-3 months for complete restructuring
|
||||
|
||||
---
|
||||
|
||||
**Analysis Methodology**: Automated codebase exploration using framework's Discovery System, manual architectural review, Clean Architecture and DDD principles assessment.
|
||||
|
||||
**Tools Used**:
|
||||
- Framework's `UnifiedDiscoveryService`
|
||||
- Custom architectural analysis scripts
|
||||
- PSR-4 compliance checker
|
||||
|
||||
**Next Steps**:
|
||||
1. Review this analysis with development team
|
||||
2. Create GitHub issues for Priority 1 fixes
|
||||
3. Establish migration timeline
|
||||
4. Update CI/CD to enforce new structure
|
||||
76
docs/claude/README.md
Normal file
76
docs/claude/README.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Claude Documentation
|
||||
|
||||
Dieses Verzeichnis enthält AI-spezifische Dokumentation für Claude Code und andere AI-Agenten, die mit dem Framework arbeiten.
|
||||
|
||||
## Zweck
|
||||
|
||||
Die Dokumentation in diesem Verzeichnis ist speziell für AI-Agenten (wie Claude Code, Cursor AI, etc.) konzipiert und enthält:
|
||||
- Framework-spezifische Kontexte und Personas
|
||||
- MCP (Model Context Protocol) Integration
|
||||
- Development Commands für AI-Agenten
|
||||
- Coding Guidelines für AI-generierten Code
|
||||
- Code-Generierungs-Beispiele
|
||||
|
||||
## Allgemeine Framework-Dokumentation
|
||||
|
||||
Für allgemeine Framework-Dokumentation, die sowohl für Entwickler als auch AI-Agenten relevant ist, siehe:
|
||||
- [Framework Dokumentation](../README.md) - Hauptdokumentation
|
||||
- [Features](../features/) - Feature-spezifische Dokumentation
|
||||
- [Guides](../guides/) - Entwickleranleitungen
|
||||
- [LiveComponents](../livecomponents/) - LiveComponents Dokumentation
|
||||
|
||||
## Wichtige Dokumentationen
|
||||
|
||||
### MCP Integration
|
||||
- [MCP Integration](mcp-integration.md) - Model Context Protocol Server und Tools
|
||||
|
||||
### Framework Personas
|
||||
- [Framework Personas](framework-personas.md) - AI Personas für Framework-Entwicklung
|
||||
|
||||
### Development Commands
|
||||
- [Development Commands](development-commands.md) - Claude Code Commands und Workflows
|
||||
|
||||
### Coding Guidelines
|
||||
- [Guidelines](guidelines.md) - AI Coding Guidelines und Best Practices
|
||||
- [Architecture](architecture.md) - Framework-Architektur für AI-Agenten
|
||||
- [Naming Conventions](naming-conventions.md) - Namenskonventionen
|
||||
|
||||
### Code Generation Examples
|
||||
- [Examples](examples/) - Code-Generierungs-Beispiele
|
||||
|
||||
## Weitere AI-spezifische Dokumentationen
|
||||
|
||||
- [Performance Profiling](performance-profiling.md) - Performance-Analyse für AI-Agenten
|
||||
- [Structured Logging](structured-logging.md) - Logging-Patterns
|
||||
- [PHP 8.5 Integration](php85-framework-integration.md) - PHP 8.5 Features
|
||||
- [PostgreSQL Features](postgresql-features.md) - PostgreSQL-spezifische Features
|
||||
- [SSE System](sse-system.md) - Server-Sent Events
|
||||
- [SSE Integration Guide](sse-integration-guide.md) - SSE Integration
|
||||
- [Sockets Module](sockets-module.md) - Socket-Kommunikation
|
||||
- [Magic Links System](magiclinks-system.md) - Magic Links Implementation
|
||||
- [View Caching System](view-caching-system.md) - View-Caching
|
||||
- [View Refactoring Plan](view-refactoring-plan.md) - View-Refactoring
|
||||
- [X-Component Syntax](x-component-syntax.md) - X-Component Template Syntax
|
||||
- [XComponent Processor](xcomponent-processor.md) - XComponent Processing
|
||||
- [Animation System](animationsystem.md) - Animation Framework
|
||||
- [Chips & Cookies](chips-cookies.md) - Cookie Management
|
||||
- [Curl OOP API](curl-oop-api.md) - HTTP Client API
|
||||
- [Deployment Architecture](deployment-architecture.md) - Deployment-Architektur
|
||||
- [ML Framework Architecture](ml-framework-architecture.md) - Machine Learning Framework
|
||||
- [POSIX System](posix-system.md) - POSIX-Integration
|
||||
- [Typed String System](typed-string-system.md) - Typed String Value Objects
|
||||
- [Framework Refactoring Recommendations](framework-refactoring-recommendations.md) - Refactoring-Empfehlungen
|
||||
|
||||
## Verwendung
|
||||
|
||||
Diese Dokumentation wird automatisch von AI-Agenten verwendet, die mit dem Framework arbeiten. Sie sollte nicht manuell bearbeitet werden, es sei denn, es handelt sich um AI-spezifische Kontexte oder Beispiele.
|
||||
|
||||
## Migration
|
||||
|
||||
Viele Dokumentationen wurden von diesem Verzeichnis in die allgemeine Dokumentationsstruktur migriert:
|
||||
- Feature-Dokumentationen → `docs/features/`
|
||||
- Guides → `docs/guides/`
|
||||
- LiveComponents → `docs/livecomponents/`
|
||||
|
||||
Siehe [Dokumentationsanalyse](../DOCUMENTATION-ANALYSIS.md) für Details zur Reorganisation.
|
||||
|
||||
585
docs/claude/animationsystem.md
Normal file
585
docs/claude/animationsystem.md
Normal file
@@ -0,0 +1,585 @@
|
||||
# Animationssystem für Console-Modul
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Animationssystem bietet eine umfassende Lösung für Animationen im Console-Modul. Es unterstützt sowohl UI-Elemente in der TUI als auch Text-Animationen im Console-Output. Das System ist modular aufgebaut, erweiterbar und vollständig in den EventLoop integriert.
|
||||
|
||||
## Features
|
||||
|
||||
- **Mehrere Animationstypen**: Fade-In/Out, Slide, Typewriter, Marquee, Pulse
|
||||
- **Keyframe-basierte Animationen**: Komplexe Animationen mit Easing-Functions
|
||||
- **Composite Animationen**: Kombination mehrerer Animationen (parallel oder sequenziell)
|
||||
- **EventLoop Integration**: Automatische Updates im Render-Loop
|
||||
- **TUI & Console-Output**: Unterstützung für beide Anwendungsfälle
|
||||
- **Factory & Builder Pattern**: Einfache Erstellung von Animationen
|
||||
|
||||
## Architektur
|
||||
|
||||
### Core-Komponenten
|
||||
|
||||
#### Animation Interface
|
||||
|
||||
Basis-Interface für alle Animationen:
|
||||
|
||||
```php
|
||||
interface Animation
|
||||
{
|
||||
public function start(): void;
|
||||
public function stop(): void;
|
||||
public function pause(): void;
|
||||
public function resume(): void;
|
||||
public function update(float $deltaTime): bool;
|
||||
public function isActive(): bool;
|
||||
public function getProgress(): float;
|
||||
public function getDuration(): float;
|
||||
public function getDelay(): float;
|
||||
public function isLooping(): bool;
|
||||
public function onStart(callable $callback): self;
|
||||
public function onComplete(callable $callback): self;
|
||||
public function onPause(callable $callback): self;
|
||||
public function onResume(callable $callback): self;
|
||||
}
|
||||
```
|
||||
|
||||
#### AnimationManager
|
||||
|
||||
Verwaltet mehrere Animationen gleichzeitig und aktualisiert sie automatisch:
|
||||
|
||||
```php
|
||||
$animationManager = new AnimationManager();
|
||||
|
||||
// Animation hinzufügen
|
||||
$animationManager->add($animation);
|
||||
|
||||
// Animationen aktualisieren (wird automatisch vom EventLoop aufgerufen)
|
||||
$animationManager->update($deltaTime);
|
||||
|
||||
// Animation entfernen
|
||||
$animationManager->remove($animation);
|
||||
|
||||
// Alle Animationen löschen
|
||||
$animationManager->clear();
|
||||
```
|
||||
|
||||
#### Easing Functions
|
||||
|
||||
Unterstützte Easing-Functions:
|
||||
|
||||
- `LINEAR` - Lineare Interpolation
|
||||
- `EASE_IN` - Langsam starten
|
||||
- `EASE_OUT` - Langsam enden
|
||||
- `EASE_IN_OUT` - Langsam starten und enden
|
||||
- `EASE_IN_QUAD` - Quadratische Beschleunigung
|
||||
- `EASE_OUT_QUAD` - Quadratische Verzögerung
|
||||
- `EASE_IN_OUT_QUAD` - Quadratische Beschleunigung und Verzögerung
|
||||
- `BOUNCE` - Bounce-Effekt
|
||||
- `ELASTIC` - Elastischer Effekt
|
||||
|
||||
## Animation-Typen
|
||||
|
||||
### FadeAnimation
|
||||
|
||||
Fade-In/Out Effekte für Text oder UI-Elemente:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\AnimationFactory;
|
||||
use App\Framework\Console\Animation\EasingFunction;
|
||||
|
||||
// Fade-In
|
||||
$fadeIn = AnimationFactory::fadeIn(1.0, EasingFunction::EASE_IN);
|
||||
|
||||
// Fade-Out
|
||||
$fadeOut = AnimationFactory::fadeOut(1.0, EasingFunction::EASE_OUT);
|
||||
|
||||
// Custom Fade
|
||||
$fade = AnimationFactory::fade(
|
||||
duration: 2.0,
|
||||
startOpacity: 0.0,
|
||||
endOpacity: 1.0,
|
||||
easing: EasingFunction::EASE_IN_OUT
|
||||
);
|
||||
```
|
||||
|
||||
### SlideAnimation
|
||||
|
||||
Slide-Effekte in verschiedene Richtungen:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\Types\SlideDirection;
|
||||
|
||||
// Von links
|
||||
$slideFromLeft = AnimationFactory::slideInFromLeft(
|
||||
distance: 20,
|
||||
duration: 1.0
|
||||
);
|
||||
|
||||
// Von rechts
|
||||
$slideFromRight = AnimationFactory::slideInFromRight(
|
||||
distance: 20,
|
||||
duration: 1.0
|
||||
);
|
||||
|
||||
// Custom Slide
|
||||
$slide = AnimationFactory::slide(
|
||||
direction: SlideDirection::UP,
|
||||
distance: 10,
|
||||
duration: 0.5,
|
||||
easing: EasingFunction::EASE_OUT
|
||||
);
|
||||
```
|
||||
|
||||
### TypewriterAnimation
|
||||
|
||||
Typewriter-Effekt für Text:
|
||||
|
||||
```php
|
||||
$typewriter = AnimationFactory::typewriter(
|
||||
text: 'Hello, this is a typewriter animation!',
|
||||
charactersPerSecond: 10.0
|
||||
);
|
||||
|
||||
// Schnell
|
||||
$fast = AnimationFactory::typewriter('Fast text', 20.0);
|
||||
|
||||
// Langsam
|
||||
$slow = AnimationFactory::typewriter('Slow text', 5.0);
|
||||
```
|
||||
|
||||
### MarqueeAnimation
|
||||
|
||||
Scrolling Text (Marquee):
|
||||
|
||||
```php
|
||||
$marquee = AnimationFactory::marquee(
|
||||
text: 'This is a scrolling marquee text',
|
||||
width: 80,
|
||||
speed: 1.0,
|
||||
loop: true
|
||||
);
|
||||
|
||||
// Schnell
|
||||
$fastMarquee = AnimationFactory::marquee('Fast scrolling', 50, 5.0);
|
||||
|
||||
// Langsam
|
||||
$slowMarquee = AnimationFactory::marquee('Slow scrolling', 50, 0.5);
|
||||
```
|
||||
|
||||
### PulseAnimation
|
||||
|
||||
Pulsing-Effekte für Hervorhebungen:
|
||||
|
||||
```php
|
||||
$pulse = AnimationFactory::pulse(
|
||||
duration: 1.0,
|
||||
scaleStart: 1.0,
|
||||
scaleEnd: 1.2,
|
||||
pulseSpeed: 2.0
|
||||
);
|
||||
|
||||
// Sanft
|
||||
$gentle = PulseAnimation::gentle(2.0);
|
||||
|
||||
// Stark
|
||||
$strong = PulseAnimation::strong(1.0);
|
||||
```
|
||||
|
||||
### KeyframeAnimation
|
||||
|
||||
Keyframe-basierte Animationen mit komplexen Interpolationen:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\AnimationFrame;
|
||||
|
||||
$keyframes = [
|
||||
new AnimationFrame(0.0, 0, EasingFunction::EASE_IN),
|
||||
new AnimationFrame(0.5, 100, EasingFunction::EASE_IN_OUT),
|
||||
new AnimationFrame(1.0, 200, EasingFunction::EASE_OUT),
|
||||
];
|
||||
|
||||
$keyframeAnimation = AnimationFactory::keyframe(
|
||||
keyframes: $keyframes,
|
||||
duration: 2.0,
|
||||
loop: false
|
||||
);
|
||||
```
|
||||
|
||||
### CompositeAnimation
|
||||
|
||||
Kombination mehrerer Animationen:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\CompositeAnimation;
|
||||
use App\Framework\Console\Animation\SequenceType;
|
||||
|
||||
$fadeIn = AnimationFactory::fadeIn(0.5);
|
||||
$slide = AnimationFactory::slideInFromLeft(20, 0.5);
|
||||
|
||||
// Parallel (gleichzeitig)
|
||||
$parallel = new CompositeAnimation(
|
||||
animations: [$fadeIn, $slide],
|
||||
sequenceType: SequenceType::PARALLEL,
|
||||
loop: false
|
||||
);
|
||||
|
||||
// Sequenziell (nacheinander)
|
||||
$sequential = new CompositeAnimation(
|
||||
animations: [$fadeIn, $slide],
|
||||
sequenceType: SequenceType::SEQUENTIAL,
|
||||
loop: false
|
||||
);
|
||||
```
|
||||
|
||||
### SpinnerAnimation
|
||||
|
||||
Animation-basierter Spinner:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\Types\SpinnerAnimation;
|
||||
use App\Framework\Console\SpinnerStyle;
|
||||
|
||||
$spinner = AnimationFactory::spinner(
|
||||
frames: SpinnerStyle::DOTS,
|
||||
message: 'Loading...',
|
||||
frameInterval: 0.1
|
||||
);
|
||||
|
||||
// Oder direkt
|
||||
$spinner = SpinnerAnimation::fromStyle(
|
||||
style: SpinnerStyle::BARS,
|
||||
message: 'Processing...',
|
||||
frameInterval: 0.1
|
||||
);
|
||||
```
|
||||
|
||||
## Verwendung in Console-Output
|
||||
|
||||
### Einfache Text-Animationen
|
||||
|
||||
```php
|
||||
use App\Framework\Console\ConsoleOutput;
|
||||
|
||||
$output = new ConsoleOutput();
|
||||
|
||||
// Fade-In
|
||||
$output->animateFadeIn('Hello World!', 1.0);
|
||||
|
||||
// Fade-Out
|
||||
$output->animateFadeOut('Goodbye!', 1.0);
|
||||
|
||||
// Typewriter
|
||||
$output->animateTypewriter('This appears character by character', 10.0);
|
||||
|
||||
// Marquee
|
||||
$output->animateMarquee('Scrolling text', 80, 1.0);
|
||||
|
||||
// Custom Animation
|
||||
$customAnimation = AnimationFactory::fadeIn(2.0);
|
||||
$output->animateText('Custom animated text', $customAnimation);
|
||||
|
||||
// Animationen aktualisieren
|
||||
$output->updateAnimations(0.016); // ~60 FPS
|
||||
```
|
||||
|
||||
### Animation Manager Zugriff
|
||||
|
||||
```php
|
||||
$animationManager = $output->getAnimationManager();
|
||||
|
||||
// Animationen direkt hinzufügen
|
||||
$animationManager->add($animation);
|
||||
|
||||
// Animationen aktualisieren
|
||||
$animationManager->update($deltaTime);
|
||||
```
|
||||
|
||||
## Verwendung in TUI
|
||||
|
||||
### TuiAnimationRenderer
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\TuiAnimationRenderer;
|
||||
use App\Framework\Console\Animation\AnimationFactory;
|
||||
use App\Framework\Console\Animation\Types\SlideDirection;
|
||||
|
||||
$animationRenderer = $tuiRenderer->getAnimationRenderer();
|
||||
|
||||
if ($animationRenderer !== null) {
|
||||
// Element animieren
|
||||
$animationRenderer->fadeInElement('menu-item-1', 0.5);
|
||||
$animationRenderer->slideInElement(
|
||||
'button-1',
|
||||
SlideDirection::LEFT,
|
||||
20,
|
||||
0.5
|
||||
);
|
||||
$animationRenderer->pulseElement('highlight-1', 1.0);
|
||||
|
||||
// Custom Animation
|
||||
$customAnimation = AnimationFactory::fadeIn(1.0);
|
||||
$animationRenderer->animateElement('element-id', $customAnimation);
|
||||
|
||||
// Animation stoppen
|
||||
$animationRenderer->stopElement('element-id');
|
||||
|
||||
// Animationswerte abrufen
|
||||
$opacity = $animationRenderer->getElementOpacity('element-id');
|
||||
$position = $animationRenderer->getElementPosition('element-id');
|
||||
$scale = $animationRenderer->getElementScale('element-id');
|
||||
}
|
||||
```
|
||||
|
||||
### Integration in TuiRenderer
|
||||
|
||||
Der `TuiRenderer` aktualisiert Animationen automatisch im Render-Loop:
|
||||
|
||||
```php
|
||||
// In TuiRenderer::render()
|
||||
if ($this->animationRenderer !== null) {
|
||||
$this->animationRenderer->update(0.016); // ~60 FPS
|
||||
}
|
||||
```
|
||||
|
||||
## Factory & Builder Pattern
|
||||
|
||||
### AnimationFactory
|
||||
|
||||
Einfache Erstellung von Animationen:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\AnimationFactory;
|
||||
|
||||
$fadeIn = AnimationFactory::fadeIn(1.0);
|
||||
$fadeOut = AnimationFactory::fadeOut(1.0);
|
||||
$slide = AnimationFactory::slideInFromLeft(20, 1.0);
|
||||
$typewriter = AnimationFactory::typewriter('Text', 10.0);
|
||||
$marquee = AnimationFactory::marquee('Text', 80, 1.0);
|
||||
$pulse = AnimationFactory::pulse(1.0);
|
||||
$spinner = AnimationFactory::spinner(SpinnerStyle::DOTS, 'Loading...');
|
||||
$keyframe = AnimationFactory::keyframe($keyframes, 2.0);
|
||||
```
|
||||
|
||||
### AnimationBuilder
|
||||
|
||||
Fluent Builder für komplexe Konfigurationen:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Animation\AnimationBuilder;
|
||||
|
||||
$animation = AnimationBuilder::fade(0.0, 1.0)
|
||||
->duration(2.0)
|
||||
->delay(0.5)
|
||||
->loop(false)
|
||||
->easing(EasingFunction::EASE_IN_OUT)
|
||||
->onStart(function ($anim) {
|
||||
echo "Animation started\n";
|
||||
})
|
||||
->onComplete(function ($anim) {
|
||||
echo "Animation completed\n";
|
||||
})
|
||||
->onPause(function ($anim) {
|
||||
echo "Animation paused\n";
|
||||
})
|
||||
->onResume(function ($anim) {
|
||||
echo "Animation resumed\n";
|
||||
})
|
||||
->build();
|
||||
```
|
||||
|
||||
## EventLoop Integration
|
||||
|
||||
Der `EventLoop` kann automatisch Animationen aktualisieren:
|
||||
|
||||
```php
|
||||
use App\Framework\Console\Components\EventLoop\EventLoop;
|
||||
use App\Framework\Console\Components\EventLoop\EventLoopConfig;
|
||||
use App\Framework\Console\Animation\AnimationManager;
|
||||
|
||||
$animationManager = new AnimationManager();
|
||||
$eventBuffer = new EventBuffer();
|
||||
$config = new EventLoopConfig();
|
||||
|
||||
// EventLoop mit AnimationManager erstellen
|
||||
$eventLoop = new EventLoop($eventBuffer, $config, $animationManager);
|
||||
|
||||
// Animationen werden automatisch aktualisiert
|
||||
// Oder manuell:
|
||||
$eventLoop->updateAnimations(0.016);
|
||||
```
|
||||
|
||||
## Callbacks
|
||||
|
||||
Animationen unterstützen Callbacks für verschiedene Events:
|
||||
|
||||
```php
|
||||
$animation = AnimationFactory::fadeIn(1.0)
|
||||
->onStart(function (Animation $anim) {
|
||||
echo "Animation started\n";
|
||||
})
|
||||
->onComplete(function (Animation $anim) {
|
||||
echo "Animation completed\n";
|
||||
})
|
||||
->onPause(function (Animation $anim) {
|
||||
echo "Animation paused\n";
|
||||
})
|
||||
->onResume(function (Animation $anim) {
|
||||
echo "Animation resumed\n";
|
||||
});
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Performance
|
||||
|
||||
1. **Animation Manager wiederverwenden**: Erstellen Sie einen `AnimationManager` pro Anwendung und verwenden Sie ihn für alle Animationen.
|
||||
|
||||
2. **Animationen entfernen**: Entfernen Sie abgeschlossene Animationen, um Speicher zu sparen:
|
||||
```php
|
||||
$animation->onComplete(function ($anim) use ($manager) {
|
||||
$manager->remove($anim);
|
||||
});
|
||||
```
|
||||
|
||||
3. **Update-Frequenz**: Aktualisieren Sie Animationen mit ~60 FPS (0.016 Sekunden):
|
||||
```php
|
||||
$animationManager->update(0.016);
|
||||
```
|
||||
|
||||
### Code-Organisation
|
||||
|
||||
1. **Factory verwenden**: Verwenden Sie `AnimationFactory` für einfache Animationen statt direkte Instanziierung.
|
||||
|
||||
2. **Builder für komplexe Animationen**: Verwenden Sie `AnimationBuilder` für komplexe Konfigurationen.
|
||||
|
||||
3. **Keyframes für komplexe Animationen**: Verwenden Sie `KeyframeAnimation` für komplexe Animationen mit mehreren Zuständen.
|
||||
|
||||
### Fehlerbehandlung
|
||||
|
||||
```php
|
||||
try {
|
||||
$animation = AnimationFactory::fadeIn(1.0);
|
||||
$animationManager->add($animation);
|
||||
$animation->start();
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
// Handle invalid animation parameters
|
||||
error_log("Animation error: " . $e->getMessage());
|
||||
}
|
||||
```
|
||||
|
||||
## Beispiel: Demo Command
|
||||
|
||||
Ein vollständiges Beispiel finden Sie in:
|
||||
|
||||
`src/Framework/Console/Examples/AnimationDemoCommand.php`
|
||||
|
||||
Ausführen mit:
|
||||
|
||||
```bash
|
||||
php console.php demo:animation
|
||||
```
|
||||
|
||||
## Erweiterung
|
||||
|
||||
### Neue Animation-Typen hinzufügen
|
||||
|
||||
1. Erstellen Sie eine neue Klasse in `src/Framework/Console/Animation/Types/`
|
||||
2. Implementieren Sie das `Animation` Interface oder erweitern Sie `BaseAnimation`
|
||||
3. Implementieren Sie `updateAnimation(float $progress)` und `getCurrentValue()`
|
||||
4. Fügen Sie Factory-Methoden zu `AnimationFactory` hinzu
|
||||
|
||||
Beispiel:
|
||||
|
||||
```php
|
||||
final class CustomAnimation extends BaseAnimation
|
||||
{
|
||||
protected function updateAnimation(float $progress): void
|
||||
{
|
||||
// Custom animation logic
|
||||
}
|
||||
|
||||
public function getCurrentValue(): mixed
|
||||
{
|
||||
// Return current animated value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API-Referenz
|
||||
|
||||
### Animation Interface
|
||||
|
||||
- `start(): void` - Startet die Animation
|
||||
- `stop(): void` - Stoppt die Animation
|
||||
- `pause(): void` - Pausiert die Animation
|
||||
- `resume(): void` - Setzt die Animation fort
|
||||
- `update(float $deltaTime): bool` - Aktualisiert die Animation
|
||||
- `isActive(): bool` - Prüft ob Animation aktiv ist
|
||||
- `getProgress(): float` - Gibt Fortschritt zurück (0.0-1.0)
|
||||
- `getDuration(): float` - Gibt Dauer zurück
|
||||
- `getDelay(): float` - Gibt Verzögerung zurück
|
||||
- `isLooping(): bool` - Prüft ob Animation loopt
|
||||
- `onStart(callable $callback): self` - Setzt Start-Callback
|
||||
- `onComplete(callable $callback): self` - Setzt Complete-Callback
|
||||
- `onPause(callable $callback): self` - Setzt Pause-Callback
|
||||
- `onResume(callable $callback): self` - Setzt Resume-Callback
|
||||
|
||||
### AnimationManager
|
||||
|
||||
- `add(Animation $animation): void` - Fügt Animation hinzu
|
||||
- `remove(Animation $animation): void` - Entfernt Animation
|
||||
- `update(float $deltaTime): void` - Aktualisiert alle Animationen
|
||||
- `clear(): void` - Löscht alle Animationen
|
||||
- `getActiveAnimations(): array` - Gibt aktive Animationen zurück
|
||||
- `getActiveCount(): int` - Gibt Anzahl aktiver Animationen zurück
|
||||
- `getTotalCount(): int` - Gibt Gesamtanzahl zurück
|
||||
|
||||
### ConsoleOutput
|
||||
|
||||
- `animateFadeIn(string $text, float $duration = 1.0): void`
|
||||
- `animateFadeOut(string $text, float $duration = 1.0): void`
|
||||
- `animateTypewriter(string $text, float $charactersPerSecond = 10.0): void`
|
||||
- `animateMarquee(string $text, int $width = 80, float $speed = 1.0): void`
|
||||
- `animateText(string $text, Animation $animation): void`
|
||||
- `updateAnimations(float $deltaTime = 0.016): void`
|
||||
- `getAnimationManager(): AnimationManager`
|
||||
|
||||
### TuiAnimationRenderer
|
||||
|
||||
- `animateElement(string $elementId, Animation $animation): void`
|
||||
- `fadeInElement(string $elementId, float $duration = 0.5): void`
|
||||
- `fadeOutElement(string $elementId, float $duration = 0.5): void`
|
||||
- `pulseElement(string $elementId, float $duration = 1.0): void`
|
||||
- `slideInElement(string $elementId, SlideDirection $direction, int $distance, float $duration = 0.5): void`
|
||||
- `stopElement(string $elementId): void`
|
||||
- `getElementOpacity(string $elementId): float`
|
||||
- `getElementPosition(string $elementId): int`
|
||||
- `getElementScale(string $elementId): float`
|
||||
- `hasAnimation(string $elementId): bool`
|
||||
- `update(float $deltaTime): void`
|
||||
- `clear(): void`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Animationen laufen nicht
|
||||
|
||||
1. **AnimationManager prüfen**: Stellen Sie sicher, dass ein `AnimationManager` vorhanden ist.
|
||||
2. **Update-Aufrufe**: Stellen Sie sicher, dass `update()` regelmäßig aufgerufen wird.
|
||||
3. **Animation starten**: Rufen Sie `start()` auf der Animation auf.
|
||||
|
||||
### Performance-Probleme
|
||||
|
||||
1. **Zu viele Animationen**: Reduzieren Sie die Anzahl gleichzeitiger Animationen.
|
||||
2. **Update-Frequenz**: Erhöhen Sie das Update-Intervall (z.B. 0.033 für 30 FPS).
|
||||
3. **Abgeschlossene Animationen**: Entfernen Sie abgeschlossene Animationen.
|
||||
|
||||
### Animationen werden nicht angezeigt
|
||||
|
||||
1. **TUI-Integration**: Für TUI-Animationen muss `TuiAnimationRenderer` verwendet werden.
|
||||
2. **Console-Output**: Für Text-Animationen muss `ConsoleOutput::animateText()` verwendet werden.
|
||||
3. **Update-Loop**: Stellen Sie sicher, dass der Update-Loop läuft.
|
||||
|
||||
## Weitere Ressourcen
|
||||
|
||||
- **Plan-Dokument**: `console-modul-refactoring.plan.md`
|
||||
- **Demo Command**: `src/Framework/Console/Examples/AnimationDemoCommand.php`
|
||||
- **Framework Guidelines**: `docs/claude/guidelines.md`
|
||||
|
||||
@@ -1,249 +0,0 @@
|
||||
# Error Handling & Debugging
|
||||
|
||||
This guide covers error handling patterns and debugging strategies in the framework.
|
||||
|
||||
## Exception Handling
|
||||
|
||||
All custom exceptions in the framework must extend `FrameworkException` to ensure consistent error handling, logging, and recovery mechanisms.
|
||||
|
||||
### The FrameworkException System
|
||||
|
||||
The framework provides a sophisticated exception system with:
|
||||
- **ExceptionContext**: Rich context information for debugging
|
||||
- **ErrorCode**: Categorized error codes with recovery hints
|
||||
- **RetryAfter**: Support for recoverable operations
|
||||
- **Fluent Interface**: Easy context building
|
||||
|
||||
### Creating Custom Exceptions
|
||||
|
||||
```php
|
||||
namespace App\Domain\User\Exceptions;
|
||||
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
use App\Framework\Exception\Core\DatabaseErrorCode;
|
||||
use App\Framework\Exception\ExceptionContext;
|
||||
|
||||
final class UserNotFoundException extends FrameworkException
|
||||
{
|
||||
public static function byId(UserId $id): self
|
||||
{
|
||||
return self::create(
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND,
|
||||
"User with ID '{$id->toString()}' not found"
|
||||
)->withData([
|
||||
'user_id' => $id->toString(),
|
||||
'search_type' => 'by_id'
|
||||
]);
|
||||
}
|
||||
|
||||
public static function byEmail(Email $email): self
|
||||
{
|
||||
$context = ExceptionContext::forOperation('user.lookup', 'UserRepository')
|
||||
->withData(['email' => $email->getMasked()]);
|
||||
|
||||
return self::fromContext(
|
||||
"User with email not found",
|
||||
$context,
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using ErrorCode Enums
|
||||
|
||||
The framework provides category-specific error code enums for better organization and type safety:
|
||||
|
||||
```php
|
||||
use App\Framework\Exception\Core\DatabaseErrorCode;
|
||||
use App\Framework\Exception\Core\AuthErrorCode;
|
||||
use App\Framework\Exception\Core\HttpErrorCode;
|
||||
use App\Framework\Exception\Core\SecurityErrorCode;
|
||||
use App\Framework\Exception\Core\ValidationErrorCode;
|
||||
|
||||
// Database errors
|
||||
DatabaseErrorCode::CONNECTION_FAILED
|
||||
DatabaseErrorCode::QUERY_FAILED
|
||||
DatabaseErrorCode::TRANSACTION_FAILED
|
||||
DatabaseErrorCode::CONSTRAINT_VIOLATION
|
||||
|
||||
// Authentication errors
|
||||
AuthErrorCode::CREDENTIALS_INVALID
|
||||
AuthErrorCode::TOKEN_EXPIRED
|
||||
AuthErrorCode::SESSION_EXPIRED
|
||||
AuthErrorCode::ACCOUNT_LOCKED
|
||||
|
||||
// HTTP errors
|
||||
HttpErrorCode::BAD_REQUEST
|
||||
HttpErrorCode::NOT_FOUND
|
||||
HttpErrorCode::METHOD_NOT_ALLOWED
|
||||
HttpErrorCode::RATE_LIMIT_EXCEEDED
|
||||
|
||||
// Security errors
|
||||
SecurityErrorCode::CSRF_TOKEN_INVALID
|
||||
SecurityErrorCode::SQL_INJECTION_DETECTED
|
||||
SecurityErrorCode::XSS_DETECTED
|
||||
SecurityErrorCode::PATH_TRAVERSAL_DETECTED
|
||||
|
||||
// Validation errors
|
||||
ValidationErrorCode::INVALID_INPUT
|
||||
ValidationErrorCode::REQUIRED_FIELD_MISSING
|
||||
ValidationErrorCode::BUSINESS_RULE_VIOLATION
|
||||
ValidationErrorCode::INVALID_FORMAT
|
||||
|
||||
// Using error codes in exceptions:
|
||||
throw FrameworkException::create(
|
||||
DatabaseErrorCode::QUERY_FAILED,
|
||||
"Failed to execute user query"
|
||||
)->withContext(
|
||||
ExceptionContext::forOperation('user.find', 'UserRepository')
|
||||
->withData(['query' => 'SELECT * FROM users WHERE id = ?'])
|
||||
->withDebug(['bind_params' => [$userId]])
|
||||
);
|
||||
```
|
||||
|
||||
### Exception Context Building
|
||||
|
||||
```php
|
||||
// Method 1: Using factory methods
|
||||
$exception = FrameworkException::forOperation(
|
||||
'payment.process',
|
||||
'PaymentService',
|
||||
'Payment processing failed',
|
||||
HttpErrorCode::BAD_GATEWAY
|
||||
)->withData([
|
||||
'amount' => $amount->toArray(),
|
||||
'gateway' => 'stripe',
|
||||
'customer_id' => $customerId
|
||||
])->withMetadata([
|
||||
'attempt' => 1,
|
||||
'idempotency_key' => $idempotencyKey
|
||||
]);
|
||||
|
||||
// Method 2: Building context separately
|
||||
$context = ExceptionContext::empty()
|
||||
->withOperation('order.validate', 'OrderService')
|
||||
->withData([
|
||||
'order_id' => $orderId,
|
||||
'total' => $total->toDecimal()
|
||||
])
|
||||
->withDebug([
|
||||
'validation_rules' => ['min_amount', 'max_items'],
|
||||
'failed_rule' => 'min_amount'
|
||||
]);
|
||||
|
||||
throw FrameworkException::fromContext(
|
||||
'Order validation failed',
|
||||
$context,
|
||||
ValidationErrorCode::BUSINESS_RULE_VIOLATION
|
||||
);
|
||||
```
|
||||
|
||||
### Recoverable Exceptions
|
||||
|
||||
```php
|
||||
// Creating recoverable exceptions with retry hints
|
||||
final class RateLimitException extends FrameworkException
|
||||
{
|
||||
public static function exceeded(int $retryAfter): self
|
||||
{
|
||||
return self::create(
|
||||
HttpErrorCode::RATE_LIMIT_EXCEEDED,
|
||||
'API rate limit exceeded'
|
||||
)->withRetryAfter($retryAfter)
|
||||
->withData(['retry_after_seconds' => $retryAfter]);
|
||||
}
|
||||
}
|
||||
|
||||
// Using in code
|
||||
try {
|
||||
$response = $apiClient->request($endpoint);
|
||||
} catch (RateLimitException $e) {
|
||||
if ($e->isRecoverable()) {
|
||||
$waitTime = $e->getRetryAfter();
|
||||
// Schedule retry after $waitTime seconds
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Categories
|
||||
|
||||
```php
|
||||
// Check exception category for handling strategies
|
||||
try {
|
||||
$result = $operation->execute();
|
||||
} catch (FrameworkException $e) {
|
||||
if ($e->isCategory('AUTH')) {
|
||||
// Handle authentication errors
|
||||
return $this->redirectToLogin();
|
||||
}
|
||||
|
||||
if ($e->isCategory('VAL')) {
|
||||
// Handle validation errors
|
||||
return $this->validationErrorResponse($e);
|
||||
}
|
||||
|
||||
if ($e->isErrorCode(DatabaseErrorCode::CONNECTION_FAILED)) {
|
||||
// Handle specific database connection errors
|
||||
$this->notifyOps($e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Simple Exceptions for Quick Use
|
||||
|
||||
```php
|
||||
// When you don't need the full context system
|
||||
throw FrameworkException::simple('Quick error message');
|
||||
|
||||
// With previous exception
|
||||
} catch (\PDOException $e) {
|
||||
throw FrameworkException::simple(
|
||||
'Database operation failed',
|
||||
$e,
|
||||
500
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Data Sanitization
|
||||
|
||||
The framework automatically sanitizes sensitive data in exceptions:
|
||||
|
||||
```php
|
||||
// Sensitive keys are automatically redacted
|
||||
$exception->withData([
|
||||
'username' => 'john@example.com',
|
||||
'password' => 'secret123', // Will be logged as '[REDACTED]'
|
||||
'api_key' => 'sk_live_...' // Will be logged as '[REDACTED]'
|
||||
]);
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Always extend FrameworkException** for custom exceptions
|
||||
2. **Use ErrorCode enum** for categorizable errors
|
||||
3. **Provide rich context** with operation, component, and data
|
||||
4. **Use factory methods** for consistent exception creation
|
||||
5. **Sanitize sensitive data** (automatic for common keys)
|
||||
6. **Make exceptions domain-specific** (UserNotFoundException vs generic NotFoundException)
|
||||
7. **Include recovery hints** for recoverable errors
|
||||
|
||||
## Logging Best Practices
|
||||
|
||||
TODO: Document logging patterns and levels
|
||||
|
||||
## Debug Strategies
|
||||
|
||||
TODO: Document debugging approaches and tools
|
||||
|
||||
## Error Recovery Patterns
|
||||
|
||||
TODO: Document error recovery and graceful degradation
|
||||
|
||||
## Common Error Scenarios
|
||||
|
||||
TODO: List common errors and solutions
|
||||
735
docs/claude/js-modules-analysis.md
Normal file
735
docs/claude/js-modules-analysis.md
Normal file
@@ -0,0 +1,735 @@
|
||||
# JavaScript Modules Analysis & Recommendations
|
||||
|
||||
**Comprehensive Analysis of Existing JS Modules and Recommendations for New Modules and Refactorings**
|
||||
|
||||
This document provides a detailed analysis of the current JavaScript module ecosystem, identifies areas for improvement, and proposes new modules and refactorings.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Current Module Overview](#current-module-overview)
|
||||
2. [Module Quality Assessment](#module-quality-assessment)
|
||||
3. [Recommended New Modules](#recommended-new-modules)
|
||||
4. [Refactoring Recommendations](#refactoring-recommendations)
|
||||
5. [Priority Matrix](#priority-matrix)
|
||||
6. [Implementation Roadmap](#implementation-roadmap)
|
||||
|
||||
---
|
||||
|
||||
## Current Module Overview
|
||||
|
||||
### Module Categories
|
||||
|
||||
#### 1. **Core Framework Modules**
|
||||
- `livecomponent/` - LiveComponents system (well-structured, modern)
|
||||
- `ui/` - UI components (Modal, Dialog, Lightbox)
|
||||
- `api-manager/` - Web API wrappers (comprehensive)
|
||||
- `sse/` - Server-Sent Events client
|
||||
|
||||
#### 2. **Form & Input Modules**
|
||||
- `form-handling/` - Form validation and submission
|
||||
- `form-autosave.js` - Auto-save functionality
|
||||
|
||||
#### 3. **Navigation & Routing**
|
||||
- `spa-router/` - Single Page Application router
|
||||
|
||||
#### 4. **Animation & Effects Modules**
|
||||
- `canvas-animations/` - Canvas-based animations
|
||||
- `scrollfx/` - Scroll-based animations
|
||||
- `parallax/` - Parallax effects
|
||||
- `smooth-scroll/` - Smooth scrolling
|
||||
- `scroll-timeline/` - Scroll timeline animations
|
||||
- `scroll-loop/` - Infinite scroll loops
|
||||
- `scroll-dependent/` - Scroll-dependent effects
|
||||
- `sticky-fade/` - Sticky fade effects
|
||||
- `sticky-steps/` - Sticky step animations
|
||||
- `inertia-scroll/` - Inertia scrolling
|
||||
- `wheel-boost/` - Wheel boost effects
|
||||
- `noise/` - Noise effects
|
||||
|
||||
#### 5. **Media & Image Modules**
|
||||
- `image-manager/` - Image gallery, upload, modal
|
||||
|
||||
#### 6. **Utility Modules**
|
||||
- `csrf-auto-refresh.js` - CSRF token management
|
||||
- `hot-reload.js` - Hot reload functionality
|
||||
- `performance-profiler/` - Performance profiling
|
||||
- `webpush/` - Web Push notifications
|
||||
|
||||
#### 7. **Admin Modules**
|
||||
- `admin/data-table.js` - Admin data tables
|
||||
|
||||
---
|
||||
|
||||
## Module Quality Assessment
|
||||
|
||||
### ✅ Well-Structured Modules (Keep as-is)
|
||||
|
||||
1. **livecomponent/** - Excellent structure, modern patterns
|
||||
- ✅ Modular architecture
|
||||
- ✅ TypeScript definitions
|
||||
- ✅ Error handling
|
||||
- ✅ Comprehensive documentation
|
||||
|
||||
2. **api-manager/** - Well-organized Web API wrappers
|
||||
- ✅ Consistent API
|
||||
- ✅ Feature detection
|
||||
- ✅ Good separation of concerns
|
||||
|
||||
3. **form-handling/** - Solid form handling
|
||||
- ✅ Clear separation (Handler, Validator, State)
|
||||
- ✅ Progressive enhancement
|
||||
- ⚠️ Could benefit from LiveComponent integration
|
||||
|
||||
4. **ui/** - Clean UI component system
|
||||
- ✅ Reusable components
|
||||
- ✅ Consistent API
|
||||
- ⚠️ Could expand with more components
|
||||
|
||||
### ⚠️ Modules Needing Refactoring
|
||||
|
||||
1. **spa-router/** - Good but could be improved
|
||||
- ⚠️ Mixed concerns (routing + transitions)
|
||||
- ⚠️ Could better integrate with LiveComponents
|
||||
- ⚠️ Module re-initialization could be cleaner
|
||||
|
||||
2. **form-autosave.js** - Standalone file
|
||||
- ⚠️ Should be part of form-handling module
|
||||
- ⚠️ No module system integration
|
||||
|
||||
3. **Scroll Animation Modules** - Too many separate modules
|
||||
- ⚠️ `scrollfx/`, `parallax/`, `scroll-timeline/`, `scroll-loop/`, `scroll-dependent/`, `sticky-fade/`, `sticky-steps/` - Could be unified
|
||||
- ⚠️ Duplicate functionality
|
||||
- ⚠️ Inconsistent APIs
|
||||
|
||||
4. **image-manager/** - Good but could be enhanced
|
||||
- ⚠️ Could integrate with LiveComponent file uploads
|
||||
- ⚠️ EventEmitter pattern could be modernized
|
||||
|
||||
5. **performance-profiler/** - Standalone
|
||||
- ⚠️ Could integrate with LiveComponent DevTools
|
||||
- ⚠️ Should be part of development tools
|
||||
|
||||
### ❌ Modules Needing Major Refactoring
|
||||
|
||||
1. **Multiple Scroll Modules** - Consolidation needed
|
||||
- ❌ 8+ separate scroll-related modules
|
||||
- ❌ Inconsistent patterns
|
||||
- ❌ Hard to maintain
|
||||
|
||||
2. **csrf-auto-refresh.js** - Standalone file
|
||||
- ❌ Should be part of security module
|
||||
- ❌ No module system integration
|
||||
|
||||
---
|
||||
|
||||
## Recommended New Modules
|
||||
|
||||
### 1. **State Management Module** (High Priority)
|
||||
|
||||
**Purpose**: Centralized state management for client-side state
|
||||
|
||||
**Features**:
|
||||
- Reactive state store (similar to Redux/Vuex)
|
||||
- State persistence (localStorage, sessionStorage)
|
||||
- State synchronization across tabs
|
||||
- Time-travel debugging
|
||||
- Integration with LiveComponents
|
||||
|
||||
**Use Cases**:
|
||||
- User preferences
|
||||
- Shopping cart state
|
||||
- UI state (sidebar open/closed, theme)
|
||||
- Form drafts
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { StateManager } from './modules/state-manager/index.js';
|
||||
|
||||
const store = StateManager.create({
|
||||
user: { name: '', email: '' },
|
||||
cart: { items: [], total: 0 },
|
||||
ui: { sidebarOpen: false }
|
||||
});
|
||||
|
||||
// Reactive updates
|
||||
store.subscribe('cart', (cart) => {
|
||||
updateCartUI(cart);
|
||||
});
|
||||
|
||||
// Actions
|
||||
store.dispatch('cart.addItem', { id: 1, name: 'Product' });
|
||||
```
|
||||
|
||||
**Priority**: High
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 2. **Validation Module** (High Priority)
|
||||
|
||||
**Purpose**: Standalone validation system (not just forms)
|
||||
|
||||
**Features**:
|
||||
- Field-level validation
|
||||
- Schema-based validation (JSON Schema)
|
||||
- Async validation
|
||||
- Custom validation rules
|
||||
- Integration with LiveComponents
|
||||
- Integration with form-handling
|
||||
|
||||
**Use Cases**:
|
||||
- Form validation
|
||||
- API response validation
|
||||
- User input validation
|
||||
- Data transformation validation
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { Validator } from './modules/validation/index.js';
|
||||
|
||||
const validator = Validator.create({
|
||||
email: {
|
||||
type: 'email',
|
||||
required: true,
|
||||
message: 'Invalid email address'
|
||||
},
|
||||
age: {
|
||||
type: 'number',
|
||||
min: 18,
|
||||
max: 100
|
||||
}
|
||||
});
|
||||
|
||||
const result = await validator.validate({ email: 'test@example.com', age: 25 });
|
||||
```
|
||||
|
||||
**Priority**: High
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 3. **Cache Manager Module** (Medium Priority)
|
||||
|
||||
**Purpose**: Intelligent caching for API responses and computed values
|
||||
|
||||
**Features**:
|
||||
- Memory cache
|
||||
- IndexedDB cache
|
||||
- Cache invalidation strategies
|
||||
- Cache warming
|
||||
- Cache analytics
|
||||
- Integration with RequestDeduplicator
|
||||
|
||||
**Use Cases**:
|
||||
- API response caching
|
||||
- Computed value caching
|
||||
- Image caching
|
||||
- Search result caching
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { CacheManager } from './modules/cache-manager/index.js';
|
||||
|
||||
const cache = CacheManager.create({
|
||||
strategy: 'stale-while-revalidate',
|
||||
ttl: 3600000 // 1 hour
|
||||
});
|
||||
|
||||
// Cache API response
|
||||
const data = await cache.get('users', async () => {
|
||||
return await fetch('/api/users').then(r => r.json());
|
||||
});
|
||||
|
||||
// Invalidate cache
|
||||
cache.invalidate('users');
|
||||
```
|
||||
|
||||
**Priority**: Medium
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 4. **Event Bus Module** (Medium Priority)
|
||||
|
||||
**Purpose**: Centralized event system for cross-module communication
|
||||
|
||||
**Features**:
|
||||
- Pub/sub pattern
|
||||
- Namespaced events
|
||||
- Event filtering
|
||||
- Event history
|
||||
- Integration with LiveComponents
|
||||
- Integration with SSE
|
||||
|
||||
**Use Cases**:
|
||||
- Component communication
|
||||
- Module communication
|
||||
- Global notifications
|
||||
- Analytics events
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { EventBus } from './modules/event-bus/index.js';
|
||||
|
||||
const bus = EventBus.create();
|
||||
|
||||
// Subscribe
|
||||
bus.on('user:logged-in', (user) => {
|
||||
updateUI(user);
|
||||
});
|
||||
|
||||
// Publish
|
||||
bus.emit('user:logged-in', { id: 1, name: 'John' });
|
||||
|
||||
// Namespaced events
|
||||
bus.on('livecomponent:action-executed', (data) => {
|
||||
console.log('Action executed:', data);
|
||||
});
|
||||
```
|
||||
|
||||
**Priority**: Medium
|
||||
**Effort**: Low (1-2 days)
|
||||
|
||||
---
|
||||
|
||||
### 5. **Storage Manager Module** (Low Priority)
|
||||
|
||||
**Purpose**: Unified storage interface (localStorage, sessionStorage, IndexedDB)
|
||||
|
||||
**Features**:
|
||||
- Unified API for all storage types
|
||||
- Automatic serialization
|
||||
- Storage quotas
|
||||
- Storage migration
|
||||
- Storage analytics
|
||||
|
||||
**Note**: Partially exists in `api-manager/StorageManager`, but could be enhanced
|
||||
|
||||
**Priority**: Low (enhance existing)
|
||||
**Effort**: Low (1 day)
|
||||
|
||||
---
|
||||
|
||||
### 6. **Router Enhancement Module** (Medium Priority)
|
||||
|
||||
**Purpose**: Enhanced routing with guards, middleware, and lazy loading
|
||||
|
||||
**Features**:
|
||||
- Route guards (auth, permissions)
|
||||
- Route middleware
|
||||
- Lazy route loading
|
||||
- Route transitions
|
||||
- Route analytics
|
||||
- Integration with LiveComponents
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { Router } from './modules/router/index.js';
|
||||
|
||||
const router = Router.create({
|
||||
routes: [
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: 'DashboardComponent',
|
||||
guard: 'auth',
|
||||
middleware: ['analytics']
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (requiresAuth(to) && !isAuthenticated()) {
|
||||
next('/login');
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Priority**: Medium
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 7. **Animation System Module** (Low Priority)
|
||||
|
||||
**Purpose**: Unified animation system consolidating all scroll/animation modules
|
||||
|
||||
**Features**:
|
||||
- Unified animation API
|
||||
- Scroll-based animations
|
||||
- Timeline animations
|
||||
- Performance optimizations
|
||||
- Integration with Web Animations API
|
||||
|
||||
**Consolidates**:
|
||||
- `scrollfx/`
|
||||
- `parallax/`
|
||||
- `scroll-timeline/`
|
||||
- `scroll-loop/`
|
||||
- `scroll-dependent/`
|
||||
- `sticky-fade/`
|
||||
- `sticky-steps/`
|
||||
- `canvas-animations/`
|
||||
|
||||
**Priority**: Low (refactoring existing)
|
||||
**Effort**: High (5-7 days)
|
||||
|
||||
---
|
||||
|
||||
### 8. **Error Tracking Module** (High Priority)
|
||||
|
||||
**Purpose**: Centralized error tracking and reporting
|
||||
|
||||
**Features**:
|
||||
- Error collection
|
||||
- Error grouping
|
||||
- Error reporting (to backend)
|
||||
- Error analytics
|
||||
- Integration with ErrorBoundary
|
||||
- Source map support
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { ErrorTracker } from './modules/error-tracking/index.js';
|
||||
|
||||
const tracker = ErrorTracker.create({
|
||||
endpoint: '/api/errors',
|
||||
sampleRate: 1.0
|
||||
});
|
||||
|
||||
// Automatic error tracking
|
||||
tracker.init();
|
||||
|
||||
// Manual error reporting
|
||||
tracker.captureException(new Error('Something went wrong'), {
|
||||
context: { userId: 123 },
|
||||
tags: { feature: 'checkout' }
|
||||
});
|
||||
```
|
||||
|
||||
**Priority**: High
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 9. **Analytics Module** (Medium Priority)
|
||||
|
||||
**Purpose**: Unified analytics system
|
||||
|
||||
**Features**:
|
||||
- Event tracking
|
||||
- Page view tracking
|
||||
- User behavior tracking
|
||||
- Custom events
|
||||
- Integration with LiveComponents
|
||||
- Privacy-compliant (GDPR)
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { Analytics } from './modules/analytics/index.js';
|
||||
|
||||
const analytics = Analytics.create({
|
||||
providers: ['google-analytics', 'custom']
|
||||
});
|
||||
|
||||
analytics.track('purchase', {
|
||||
value: 99.99,
|
||||
currency: 'EUR',
|
||||
items: [{ id: 'product-1', quantity: 1 }]
|
||||
});
|
||||
```
|
||||
|
||||
**Priority**: Medium
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 10. **Internationalization (i18n) Module** (Low Priority)
|
||||
|
||||
**Purpose**: Internationalization and localization
|
||||
|
||||
**Features**:
|
||||
- Translation management
|
||||
- Pluralization
|
||||
- Date/time formatting
|
||||
- Number formatting
|
||||
- Currency formatting
|
||||
- Integration with LiveComponents
|
||||
|
||||
**API Example**:
|
||||
```javascript
|
||||
import { i18n } from './modules/i18n/index.js';
|
||||
|
||||
i18n.init({
|
||||
locale: 'de-DE',
|
||||
fallback: 'en-US',
|
||||
translations: {
|
||||
'de-DE': { 'welcome': 'Willkommen' },
|
||||
'en-US': { 'welcome': 'Welcome' }
|
||||
}
|
||||
});
|
||||
|
||||
const message = i18n.t('welcome');
|
||||
```
|
||||
|
||||
**Priority**: Low
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
## Refactoring Recommendations
|
||||
|
||||
### 1. **Consolidate Scroll Animation Modules** (High Priority)
|
||||
|
||||
**Current State**: 8+ separate scroll-related modules
|
||||
|
||||
**Proposed Solution**: Create unified `animation-system/` module
|
||||
|
||||
**Benefits**:
|
||||
- Single API for all animations
|
||||
- Reduced bundle size
|
||||
- Easier maintenance
|
||||
- Better performance
|
||||
- Consistent patterns
|
||||
|
||||
**Implementation**:
|
||||
1. Create `animation-system/` module
|
||||
2. Migrate functionality from existing modules
|
||||
3. Maintain backward compatibility during transition
|
||||
4. Deprecate old modules
|
||||
5. Update documentation
|
||||
|
||||
**Priority**: High
|
||||
**Effort**: High (5-7 days)
|
||||
|
||||
---
|
||||
|
||||
### 2. **Integrate form-autosave into form-handling** (Medium Priority)
|
||||
|
||||
**Current State**: Standalone `form-autosave.js` file
|
||||
|
||||
**Proposed Solution**: Add autosave functionality to `form-handling/` module
|
||||
|
||||
**Benefits**:
|
||||
- Better organization
|
||||
- Shared form state
|
||||
- Consistent API
|
||||
- Easier maintenance
|
||||
|
||||
**Implementation**:
|
||||
1. Move autosave logic into `FormHandler`
|
||||
2. Add autosave configuration options
|
||||
3. Integrate with `FormState`
|
||||
4. Update documentation
|
||||
|
||||
**Priority**: Medium
|
||||
**Effort**: Low (1 day)
|
||||
|
||||
---
|
||||
|
||||
### 3. **Enhance SPA Router with LiveComponent Integration** (Medium Priority)
|
||||
|
||||
**Current State**: SPA Router works independently
|
||||
|
||||
**Proposed Solution**: Better integration with LiveComponents
|
||||
|
||||
**Benefits**:
|
||||
- Automatic LiveComponent initialization after navigation
|
||||
- Better state management
|
||||
- Improved performance
|
||||
- Unified API
|
||||
|
||||
**Implementation**:
|
||||
1. Add LiveComponent auto-initialization
|
||||
2. Integrate with LiveComponentManager
|
||||
3. Handle component state during navigation
|
||||
4. Update documentation
|
||||
|
||||
**Priority**: Medium
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 4. **Create Security Module** (Medium Priority)
|
||||
|
||||
**Current State**: `csrf-auto-refresh.js` is standalone
|
||||
|
||||
**Proposed Solution**: Create `security/` module
|
||||
|
||||
**Features**:
|
||||
- CSRF token management
|
||||
- XSS protection helpers
|
||||
- Content Security Policy helpers
|
||||
- Security headers validation
|
||||
|
||||
**Implementation**:
|
||||
1. Create `security/` module
|
||||
2. Move CSRF logic
|
||||
3. Add additional security features
|
||||
4. Update documentation
|
||||
|
||||
**Priority**: Medium
|
||||
**Effort**: Low (1-2 days)
|
||||
|
||||
---
|
||||
|
||||
### 5. **Integrate Performance Profiler with DevTools** (Low Priority)
|
||||
|
||||
**Current State**: Standalone `performance-profiler/` module
|
||||
|
||||
**Proposed Solution**: Integrate with LiveComponent DevTools
|
||||
|
||||
**Benefits**:
|
||||
- Unified developer experience
|
||||
- Better visualization
|
||||
- Easier debugging
|
||||
- Reduced bundle size (dev only)
|
||||
|
||||
**Implementation**:
|
||||
1. Move profiler into DevTools
|
||||
2. Integrate with LiveComponent profiling
|
||||
3. Update UI
|
||||
4. Update documentation
|
||||
|
||||
**Priority**: Low
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
### 6. **Modernize Image Manager** (Low Priority)
|
||||
|
||||
**Current State**: Uses EventEmitter pattern
|
||||
|
||||
**Proposed Solution**: Modernize with ES6 classes and better integration
|
||||
|
||||
**Benefits**:
|
||||
- Modern patterns
|
||||
- Better TypeScript support
|
||||
- Integration with LiveComponent file uploads
|
||||
- Improved performance
|
||||
|
||||
**Implementation**:
|
||||
1. Refactor to ES6 classes
|
||||
2. Add TypeScript definitions
|
||||
3. Integrate with LiveComponent file uploads
|
||||
4. Update documentation
|
||||
|
||||
**Priority**: Low
|
||||
**Effort**: Medium (2-3 days)
|
||||
|
||||
---
|
||||
|
||||
## Priority Matrix
|
||||
|
||||
### High Priority (Implement Soon)
|
||||
|
||||
1. **State Management Module** - Needed for complex applications
|
||||
2. **Validation Module** - Reusable validation logic
|
||||
3. **Error Tracking Module** - Production debugging
|
||||
4. **Consolidate Scroll Animation Modules** - Maintenance burden
|
||||
|
||||
### Medium Priority (Implement Next)
|
||||
|
||||
1. **Cache Manager Module** - Performance optimization
|
||||
2. **Event Bus Module** - Cross-module communication
|
||||
3. **Router Enhancement Module** - Better routing features
|
||||
4. **Analytics Module** - Business requirements
|
||||
5. **Integrate form-autosave** - Code organization
|
||||
6. **Enhance SPA Router** - Better integration
|
||||
7. **Create Security Module** - Security best practices
|
||||
|
||||
### Low Priority (Nice to Have)
|
||||
|
||||
1. **Storage Manager Module** - Enhance existing
|
||||
2. **Animation System Module** - Refactoring existing
|
||||
3. **i18n Module** - Internationalization
|
||||
4. **Integrate Performance Profiler** - Developer experience
|
||||
5. **Modernize Image Manager** - Code quality
|
||||
|
||||
---
|
||||
|
||||
## Implementation Roadmap
|
||||
|
||||
### Phase 1: Foundation (Weeks 1-2)
|
||||
|
||||
1. **State Management Module** (3 days)
|
||||
2. **Validation Module** (3 days)
|
||||
3. **Error Tracking Module** (3 days)
|
||||
4. **Event Bus Module** (2 days)
|
||||
|
||||
**Total**: ~11 days
|
||||
|
||||
### Phase 2: Integration & Refactoring (Weeks 3-4)
|
||||
|
||||
1. **Integrate form-autosave** (1 day)
|
||||
2. **Enhance SPA Router** (3 days)
|
||||
3. **Create Security Module** (2 days)
|
||||
4. **Cache Manager Module** (3 days)
|
||||
|
||||
**Total**: ~9 days
|
||||
|
||||
### Phase 3: Advanced Features (Weeks 5-6)
|
||||
|
||||
1. **Router Enhancement Module** (3 days)
|
||||
2. **Analytics Module** (3 days)
|
||||
3. **Consolidate Scroll Animation Modules** (7 days)
|
||||
|
||||
**Total**: ~13 days
|
||||
|
||||
### Phase 4: Polish & Optimization (Weeks 7-8)
|
||||
|
||||
1. **Storage Manager Enhancement** (1 day)
|
||||
2. **Integrate Performance Profiler** (3 days)
|
||||
3. **Modernize Image Manager** (3 days)
|
||||
4. **Documentation updates** (2 days)
|
||||
|
||||
**Total**: ~9 days
|
||||
|
||||
---
|
||||
|
||||
## Module Architecture Principles
|
||||
|
||||
### 1. **Consistency**
|
||||
- All modules should follow the same structure
|
||||
- Consistent naming conventions
|
||||
- Consistent API patterns
|
||||
|
||||
### 2. **Modularity**
|
||||
- Modules should be independent
|
||||
- Clear dependencies
|
||||
- Easy to test in isolation
|
||||
|
||||
### 3. **Integration**
|
||||
- Modules should integrate well with LiveComponents
|
||||
- Shared configuration
|
||||
- Unified event system
|
||||
|
||||
### 4. **Performance**
|
||||
- Lazy loading where possible
|
||||
- Tree-shaking support
|
||||
- Minimal bundle size
|
||||
|
||||
### 5. **Developer Experience**
|
||||
- TypeScript definitions
|
||||
- Comprehensive documentation
|
||||
- Clear error messages
|
||||
- DevTools integration
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Review this analysis** with the team
|
||||
2. **Prioritize modules** based on project needs
|
||||
3. **Create detailed implementation plans** for high-priority modules
|
||||
4. **Start with Phase 1** (Foundation modules)
|
||||
5. **Iterate and refine** based on feedback
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-01-XX
|
||||
**Status**: Draft - Pending Review
|
||||
|
||||
@@ -1,681 +0,0 @@
|
||||
# LiveComponent Lazy Loading
|
||||
|
||||
**Performance-Optimization durch Viewport-basiertes Component Loading**
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Lazy Loading System lädt LiveComponents erst, wenn sie im Browser-Viewport sichtbar werden. Dies reduziert die initiale Ladezeit erheblich, besonders bei Seiten mit vielen Components.
|
||||
|
||||
**Key Features:**
|
||||
- ⚡ **Viewport-Detection** - Intersection Observer API
|
||||
- 🎯 **Priority-Based Loading** - High/Normal/Low Prioritäten
|
||||
- 🔄 **Progressive Loading** - Sequentielle Queue-Verarbeitung
|
||||
- 📊 **Loading States** - Placeholder → Loading → Loaded
|
||||
- 🧹 **Automatic Cleanup** - Memory-efficient observer management
|
||||
|
||||
## Performance Benefits
|
||||
|
||||
| Metric | Without Lazy Loading | With Lazy Loading | Improvement |
|
||||
|--------|---------------------|-------------------|-------------|
|
||||
| Initial Page Load | 2500ms | 800ms | **68% faster** |
|
||||
| Time to Interactive | 3200ms | 1100ms | **66% faster** |
|
||||
| Initial JavaScript | 450KB | 120KB | **73% smaller** |
|
||||
| Components Loaded | All (20) | Visible (3-5) | **75% fewer** |
|
||||
| Memory Usage | 120MB | 35MB | **71% less** |
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Basic Lazy Component
|
||||
|
||||
```html
|
||||
<!-- Regular Component (loads immediately) -->
|
||||
<div data-live-component="notification-center:user-123"
|
||||
data-state='{"notifications": []}'
|
||||
data-csrf-token="...">
|
||||
<!-- Component HTML -->
|
||||
</div>
|
||||
|
||||
<!-- Lazy Component (loads on viewport entry) -->
|
||||
<div data-live-component-lazy="notification-center:user-123"
|
||||
data-lazy-threshold="0.1"
|
||||
data-lazy-priority="normal"
|
||||
data-lazy-placeholder="Loading notifications...">
|
||||
</div>
|
||||
```
|
||||
|
||||
**Key Differences:**
|
||||
- Use `data-live-component-lazy` instead of `data-live-component`
|
||||
- No initial HTML needed (placeholder will be shown)
|
||||
- No `data-state` needed initially (server will provide it)
|
||||
|
||||
### 2. Priority Levels
|
||||
|
||||
```html
|
||||
<!-- High Priority - Loads first -->
|
||||
<div data-live-component-lazy="user-stats:123"
|
||||
data-lazy-priority="high"
|
||||
data-lazy-placeholder="Loading user stats...">
|
||||
</div>
|
||||
|
||||
<!-- Normal Priority - Standard loading (default) -->
|
||||
<div data-live-component-lazy="activity-feed:user-123"
|
||||
data-lazy-priority="normal"
|
||||
data-lazy-placeholder="Loading activities...">
|
||||
</div>
|
||||
|
||||
<!-- Low Priority - Loads last -->
|
||||
<div data-live-component-lazy="recommendations:user-123"
|
||||
data-lazy-priority="low"
|
||||
data-lazy-placeholder="Loading recommendations...">
|
||||
</div>
|
||||
```
|
||||
|
||||
**Priority Weights:**
|
||||
- `high`: 3 (Critical components, always load first)
|
||||
- `normal`: 2 (Standard components, default)
|
||||
- `low`: 1 (Non-critical components, load last)
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Viewport Threshold
|
||||
|
||||
Controls when loading triggers (0.0 = top edge, 1.0 = fully visible):
|
||||
|
||||
```html
|
||||
<!-- Load when 10% visible (default) -->
|
||||
<div data-live-component-lazy="counter:demo"
|
||||
data-lazy-threshold="0.1">
|
||||
</div>
|
||||
|
||||
<!-- Load when 50% visible -->
|
||||
<div data-live-component-lazy="chart:demo"
|
||||
data-lazy-threshold="0.5">
|
||||
</div>
|
||||
|
||||
<!-- Load when fully visible -->
|
||||
<div data-live-component-lazy="video-player:demo"
|
||||
data-lazy-threshold="1.0">
|
||||
</div>
|
||||
```
|
||||
|
||||
### Root Margin (Pre-loading)
|
||||
|
||||
Global configuration in LazyComponentLoader:
|
||||
|
||||
```javascript
|
||||
this.defaultOptions = {
|
||||
rootMargin: '50px', // Load 50px before entering viewport
|
||||
threshold: 0.1 // Default threshold
|
||||
};
|
||||
```
|
||||
|
||||
**Effect:** Components start loading before user scrolls to them, creating seamless experience.
|
||||
|
||||
### Custom Placeholder
|
||||
|
||||
```html
|
||||
<!-- Simple text placeholder -->
|
||||
<div data-live-component-lazy="notifications:123"
|
||||
data-lazy-placeholder="Loading your notifications...">
|
||||
</div>
|
||||
|
||||
<!-- No placeholder (just loading indicator) -->
|
||||
<div data-live-component-lazy="stats:123">
|
||||
</div>
|
||||
```
|
||||
|
||||
## Loading States
|
||||
|
||||
The system automatically manages 3 loading states:
|
||||
|
||||
### 1. Placeholder State
|
||||
|
||||
Shown immediately when component is registered:
|
||||
|
||||
```html
|
||||
<div class="livecomponent-lazy-placeholder">
|
||||
<div>⏳</div>
|
||||
<div>Loading notifications...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 2. Loading State
|
||||
|
||||
Shown when component enters viewport and server request starts:
|
||||
|
||||
```html
|
||||
<div class="livecomponent-lazy-loading">
|
||||
<div class="spinner"></div>
|
||||
<div>Loading component...</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 3. Loaded State
|
||||
|
||||
Component HTML from server replaces placeholder:
|
||||
|
||||
```html
|
||||
<div data-live-component="notification-center:user-123" ...>
|
||||
<!-- Full component HTML with actions, state, etc. -->
|
||||
</div>
|
||||
```
|
||||
|
||||
## Backend Implementation
|
||||
|
||||
### Controller Route
|
||||
|
||||
```php
|
||||
#[Route('/live-component/{id}/lazy-load', method: Method::GET)]
|
||||
public function handleLazyLoad(string $id, HttpRequest $request): JsonResult
|
||||
{
|
||||
try {
|
||||
$componentId = ComponentId::fromString($id);
|
||||
|
||||
// Resolve component with initial state
|
||||
$component = $this->componentRegistry->resolve(
|
||||
$componentId,
|
||||
initialData: null
|
||||
);
|
||||
|
||||
// Render component HTML with wrapper
|
||||
$html = $this->componentRegistry->renderWithWrapper($component);
|
||||
|
||||
// Get component state
|
||||
$componentData = $component->getData();
|
||||
|
||||
// Generate CSRF token for component
|
||||
$csrfToken = $this->componentRegistry->generateCsrfToken($componentId);
|
||||
|
||||
return new JsonResult([
|
||||
'success' => true,
|
||||
'html' => $html,
|
||||
'state' => $componentData->toArray(),
|
||||
'csrf_token' => $csrfToken,
|
||||
'component_id' => $componentId->toString()
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return new JsonResult([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage(),
|
||||
'error_code' => 'LAZY_LOAD_FAILED'
|
||||
], Status::INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"html": "<div data-live-component=\"notification-center:user-123\" ...>...</div>",
|
||||
"state": {
|
||||
"notifications": [...]
|
||||
},
|
||||
"csrf_token": "abc123...",
|
||||
"component_id": "notification-center:user-123"
|
||||
}
|
||||
```
|
||||
|
||||
## JavaScript API
|
||||
|
||||
### LazyComponentLoader Class
|
||||
|
||||
```javascript
|
||||
import { LazyComponentLoader } from './LazyComponentLoader.js';
|
||||
|
||||
// Create loader instance
|
||||
const lazyLoader = new LazyComponentLoader(liveComponentManager);
|
||||
|
||||
// Initialize system (scans DOM for lazy components)
|
||||
lazyLoader.init();
|
||||
|
||||
// Register new lazy component dynamically
|
||||
lazyLoader.registerLazyComponent(element);
|
||||
|
||||
// Unregister lazy component
|
||||
lazyLoader.unregister(element);
|
||||
|
||||
// Get loading statistics
|
||||
const stats = lazyLoader.getStats();
|
||||
console.log(stats);
|
||||
// {
|
||||
// total: 10,
|
||||
// loaded: 3,
|
||||
// loading: 1,
|
||||
// pending: 6,
|
||||
// queued: 2
|
||||
// }
|
||||
|
||||
// Cleanup and destroy
|
||||
lazyLoader.destroy();
|
||||
```
|
||||
|
||||
### Events
|
||||
|
||||
Listen for lazy loading events:
|
||||
|
||||
```javascript
|
||||
// Component successfully loaded
|
||||
document.addEventListener('livecomponent:lazy:loaded', (e) => {
|
||||
console.log('Loaded:', e.detail.componentId);
|
||||
// Custom post-load logic
|
||||
});
|
||||
|
||||
// Component load failed
|
||||
document.addEventListener('livecomponent:lazy:error', (e) => {
|
||||
console.error('Error:', e.detail.componentId, e.detail.error);
|
||||
// Error handling, retry logic, etc.
|
||||
});
|
||||
```
|
||||
|
||||
### Manual Loading
|
||||
|
||||
```javascript
|
||||
// Force load a specific component
|
||||
const config = lazyLoader.lazyComponents.get(element);
|
||||
if (config && !config.loaded) {
|
||||
await lazyLoader.loadComponent(config);
|
||||
}
|
||||
|
||||
// Process loading queue immediately (bypasses delays)
|
||||
await lazyLoader.processLoadingQueue();
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
### Loading Flow
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ DOM Scan │
|
||||
│ (init) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Register Lazy │
|
||||
│ Components │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Show Placeholder│
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Start Observing │
|
||||
│ (Intersection │
|
||||
│ Observer) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐ No
|
||||
│ Intersecting? ├─────────► Continue Observing
|
||||
└────────┬────────┘
|
||||
│ Yes
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Queue Component │
|
||||
│ (Priority-based)│
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Process Queue │
|
||||
│ (Sequential) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Show Loading │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Fetch from │
|
||||
│ Server │
|
||||
│ GET /lazy-load │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Replace HTML │
|
||||
│ Initialize │
|
||||
│ LiveComponent │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Stop Observing │
|
||||
│ Mark as Loaded │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### Priority Queue System
|
||||
|
||||
```javascript
|
||||
// When component enters viewport
|
||||
queueComponentLoad(config) {
|
||||
const priorityWeight = this.getPriorityWeight(config.priority);
|
||||
|
||||
this.loadingQueue.push({
|
||||
config,
|
||||
priority: priorityWeight,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// Sort by priority (high to low), then timestamp (early to late)
|
||||
this.loadingQueue.sort((a, b) => {
|
||||
if (b.priority !== a.priority) {
|
||||
return b.priority - a.priority;
|
||||
}
|
||||
return a.timestamp - b.timestamp;
|
||||
});
|
||||
|
||||
this.processLoadingQueue();
|
||||
}
|
||||
```
|
||||
|
||||
**Queue Order Example:**
|
||||
```
|
||||
Queue: [
|
||||
{ priority: 3, timestamp: 1000 }, // High, entered first
|
||||
{ priority: 3, timestamp: 1100 }, // High, entered second
|
||||
{ priority: 2, timestamp: 900 }, // Normal, entered early
|
||||
{ priority: 1, timestamp: 1200 } // Low, entered last
|
||||
]
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
### 1. Long Landing Pages
|
||||
|
||||
```html
|
||||
<!-- Above the fold - immediate -->
|
||||
<div data-live-component="hero:banner">...</div>
|
||||
<div data-live-component="features:overview">...</div>
|
||||
|
||||
<!-- Below the fold - lazy -->
|
||||
<div data-live-component-lazy="testimonials:featured"
|
||||
data-lazy-priority="normal">
|
||||
</div>
|
||||
|
||||
<div data-live-component-lazy="pricing:table"
|
||||
data-lazy-priority="low">
|
||||
</div>
|
||||
|
||||
<div data-live-component-lazy="faq:list"
|
||||
data-lazy-priority="low">
|
||||
</div>
|
||||
```
|
||||
|
||||
### 2. Dashboard with Widgets
|
||||
|
||||
```html
|
||||
<!-- Critical widgets - high priority -->
|
||||
<div data-live-component-lazy="user-stats:123"
|
||||
data-lazy-priority="high"
|
||||
data-lazy-threshold="0.1">
|
||||
</div>
|
||||
|
||||
<div data-live-component-lazy="notifications:123"
|
||||
data-lazy-priority="high"
|
||||
data-lazy-threshold="0.1">
|
||||
</div>
|
||||
|
||||
<!-- Secondary widgets - normal priority -->
|
||||
<div data-live-component-lazy="activity-feed:123"
|
||||
data-lazy-priority="normal">
|
||||
</div>
|
||||
|
||||
<!-- Tertiary widgets - low priority -->
|
||||
<div data-live-component-lazy="recommendations:123"
|
||||
data-lazy-priority="low">
|
||||
</div>
|
||||
```
|
||||
|
||||
### 3. Infinite Scroll
|
||||
|
||||
```html
|
||||
<!-- Initial visible items -->
|
||||
<div data-live-component="product-card:1">...</div>
|
||||
<div data-live-component="product-card:2">...</div>
|
||||
<div data-live-component="product-card:3">...</div>
|
||||
|
||||
<!-- Lazy load next batch -->
|
||||
<div data-live-component-lazy="product-card:4"
|
||||
data-lazy-threshold="0.5">
|
||||
</div>
|
||||
<div data-live-component-lazy="product-card:5"
|
||||
data-lazy-threshold="0.5">
|
||||
</div>
|
||||
```
|
||||
|
||||
### 4. Tab Panels
|
||||
|
||||
```html
|
||||
<div class="tabs">
|
||||
<!-- Active tab - immediate -->
|
||||
<div class="tab-panel active">
|
||||
<div data-live-component="profile:user-123">...</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden tabs - lazy load when shown -->
|
||||
<div class="tab-panel">
|
||||
<div data-live-component-lazy="settings:user-123"
|
||||
data-lazy-priority="normal">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-panel">
|
||||
<div data-live-component-lazy="activity-history:user-123"
|
||||
data-lazy-priority="low">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### ✅ DO
|
||||
|
||||
**Use lazy loading for:**
|
||||
- Below-the-fold components
|
||||
- Secondary/tertiary content
|
||||
- Heavy components (charts, tables, media)
|
||||
- Non-critical features
|
||||
- Infrequently accessed sections
|
||||
|
||||
**Priority Guidelines:**
|
||||
- `high`: User-specific data, real-time updates
|
||||
- `normal`: Standard content, common features
|
||||
- `low`: Recommendations, suggestions, ads
|
||||
|
||||
**Threshold Guidelines:**
|
||||
- `0.1`: Standard (load early for smooth UX)
|
||||
- `0.5`: Conservative (load when half-visible)
|
||||
- `1.0`: Aggressive (only when fully visible)
|
||||
|
||||
### ❌ DON'T
|
||||
|
||||
**Avoid lazy loading for:**
|
||||
- Above-the-fold content
|
||||
- Critical user interactions
|
||||
- SEO-important content
|
||||
- Small/lightweight components
|
||||
|
||||
**Anti-patterns:**
|
||||
- Setting all components to `high` priority
|
||||
- Using threshold `1.0` for everything
|
||||
- Lazy loading components user immediately needs
|
||||
- Over-complicating with too many priority levels
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Server-Side
|
||||
|
||||
```php
|
||||
// Cache component HTML for repeated lazy loads
|
||||
public function handleLazyLoad(string $id): JsonResult
|
||||
{
|
||||
$cacheKey = "lazy_component_{$id}";
|
||||
|
||||
return $this->cache->remember($cacheKey, function() use ($id) {
|
||||
$component = $this->componentRegistry->resolve(
|
||||
ComponentId::fromString($id)
|
||||
);
|
||||
|
||||
return new JsonResult([
|
||||
'success' => true,
|
||||
'html' => $this->componentRegistry->renderWithWrapper($component),
|
||||
// ... other data
|
||||
]);
|
||||
}, Duration::fromMinutes(5));
|
||||
}
|
||||
```
|
||||
|
||||
### Client-Side
|
||||
|
||||
```javascript
|
||||
// Adjust root margin for faster pre-loading
|
||||
this.defaultOptions = {
|
||||
rootMargin: '200px', // Start loading 200px early
|
||||
threshold: 0.1
|
||||
};
|
||||
|
||||
// Batch multiple components entering viewport simultaneously
|
||||
// (Already implemented in processLoadingQueue)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Component doesn't load
|
||||
|
||||
**Check:**
|
||||
1. `data-live-component-lazy` attribute present?
|
||||
2. LazyLoader initialized? (`window.LiveComponent.lazyLoader`)
|
||||
3. Element visible in viewport?
|
||||
4. Network request succeeding? (check DevTools)
|
||||
5. Server route `/live-component/{id}/lazy-load` working?
|
||||
|
||||
**Debug:**
|
||||
```javascript
|
||||
const stats = window.LiveComponent.lazyLoader.getStats();
|
||||
console.log(stats); // Check pending/loading/loaded counts
|
||||
|
||||
// Check if component is registered
|
||||
const config = window.LiveComponent.lazyLoader.lazyComponents.get(element);
|
||||
console.log(config);
|
||||
```
|
||||
|
||||
### Component loads too late/early
|
||||
|
||||
**Adjust threshold:**
|
||||
```html
|
||||
<!-- Load earlier (when 10% visible instead of 50%) -->
|
||||
<div data-live-component-lazy="..."
|
||||
data-lazy-threshold="0.1">
|
||||
</div>
|
||||
```
|
||||
|
||||
**Adjust root margin:**
|
||||
```javascript
|
||||
// Global configuration
|
||||
this.defaultOptions.rootMargin = '100px'; // Load 100px before viewport
|
||||
```
|
||||
|
||||
### Priority not working
|
||||
|
||||
**Check queue:**
|
||||
```javascript
|
||||
console.log(window.LiveComponent.lazyLoader.loadingQueue);
|
||||
// Should be sorted by priority (high to low)
|
||||
```
|
||||
|
||||
**Verify priority value:**
|
||||
```html
|
||||
<div data-lazy-priority="high"> ✅ Correct
|
||||
<div data-lazy-priority="High"> ❌ Case-sensitive!
|
||||
<div data-lazy-priority="urgent"> ❌ Only high/normal/low
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
### Manual Testing
|
||||
|
||||
```bash
|
||||
# Open test page in browser
|
||||
php tests/debug/test-lazy-loading.php
|
||||
# Navigate to http://localhost/tests/debug/test-lazy-loading.php
|
||||
```
|
||||
|
||||
**Test Checklist:**
|
||||
- [ ] Placeholders shown immediately
|
||||
- [ ] Components load when scrolling into view
|
||||
- [ ] High priority loads before low priority
|
||||
- [ ] Loading spinner appears briefly
|
||||
- [ ] Stats panel updates correctly
|
||||
- [ ] Console logs show loading sequence
|
||||
- [ ] No console errors
|
||||
|
||||
### Automated Testing
|
||||
|
||||
```javascript
|
||||
describe('LazyComponentLoader', () => {
|
||||
it('registers lazy components', () => {
|
||||
const loader = new LazyComponentLoader(mockManager);
|
||||
loader.init();
|
||||
|
||||
const lazyElements = document.querySelectorAll('[data-live-component-lazy]');
|
||||
expect(loader.lazyComponents.size).toBe(lazyElements.length);
|
||||
});
|
||||
|
||||
it('loads component on intersection', async () => {
|
||||
const loader = new LazyComponentLoader(mockManager);
|
||||
loader.init();
|
||||
|
||||
// Simulate intersection
|
||||
const entry = { isIntersecting: true, target: lazyElement };
|
||||
loader.handleIntersection([entry]);
|
||||
|
||||
// Wait for load
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
|
||||
const config = loader.lazyComponents.get(lazyElement);
|
||||
expect(config.loaded).toBe(true);
|
||||
});
|
||||
|
||||
it('respects priority ordering', () => {
|
||||
const loader = new LazyComponentLoader(mockManager);
|
||||
|
||||
loader.queueComponentLoad({ priority: 'low', ...lowConfig });
|
||||
loader.queueComponentLoad({ priority: 'high', ...highConfig });
|
||||
loader.queueComponentLoad({ priority: 'normal', ...normalConfig });
|
||||
|
||||
expect(loader.loadingQueue[0].config).toBe(highConfig);
|
||||
expect(loader.loadingQueue[1].config).toBe(normalConfig);
|
||||
expect(loader.loadingQueue[2].config).toBe(lowConfig);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
**Lazy Loading provides:**
|
||||
- ✅ **68% faster** initial page load
|
||||
- ✅ **73% smaller** initial JavaScript bundle
|
||||
- ✅ **75% fewer** components loaded initially
|
||||
- ✅ **71% less** memory usage
|
||||
- ✅ **Seamless UX** with priority-based loading
|
||||
- ✅ **Easy integration** with minimal code changes
|
||||
|
||||
**When to use:**
|
||||
- Pages with 10+ components
|
||||
- Below-the-fold content
|
||||
- Heavy components (charts, tables, media)
|
||||
- Secondary/tertiary features
|
||||
|
||||
**Implementation effort:** ⚡ **Low** - Just change `data-live-component` to `data-live-component-lazy`
|
||||
1316
docs/claude/posix-system.md
Normal file
1316
docs/claude/posix-system.md
Normal file
File diff suppressed because it is too large
Load Diff
671
docs/claude/sockets-module.md
Normal file
671
docs/claude/sockets-module.md
Normal file
@@ -0,0 +1,671 @@
|
||||
# Sockets Module Documentation
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Sockets-Modul (`src/Framework/Sockets/`) bietet eine vollständige, type-safe Abstraktion über PHP's Socket-Erweiterung. Es ermöglicht:
|
||||
|
||||
- **Type-safe Socket-Operationen**: Alle Socket-Operationen verwenden Value Objects statt primitiver Typen
|
||||
- **TCP/UDP Server & Client**: Vollständige Server- und Client-Implementierung
|
||||
- **Unix Domain Sockets**: Unterstützung für Unix Domain Sockets
|
||||
- **PCNTL Integration**: Socket-Server in geforkten Worker-Prozessen
|
||||
- **Async Integration**: Non-blocking Socket I/O mit Fibers
|
||||
- **Connection Pooling**: Automatisches Management von Socket-Verbindungen
|
||||
|
||||
## Architektur
|
||||
|
||||
### Modulstruktur
|
||||
|
||||
```
|
||||
src/Framework/Sockets/
|
||||
├── SocketService.php # Haupt-Service (Facade)
|
||||
├── SocketFactory.php # Socket-Erstellung
|
||||
├── SocketServer.php # Server-Operationen
|
||||
├── SocketClient.php # Client-Operationen
|
||||
├── SocketConnection.php # Connection Wrapper
|
||||
├── SocketConnectionPool.php # Connection Pool Management
|
||||
├── SocketsInitializer.php # DI-Initializer
|
||||
├── Exceptions/ # Exception-Klassen
|
||||
├── ValueObjects/ # Type-safe Value Objects
|
||||
└── Integration/ # PCNTL & Async Integration
|
||||
```
|
||||
|
||||
## Value Objects
|
||||
|
||||
### SocketAddress
|
||||
|
||||
Repräsentiert eine Socket-Adresse (IPv4, IPv6 oder Unix Domain Socket).
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
|
||||
// IPv4 Adresse
|
||||
$address = SocketAddress::ipv4('127.0.0.1', 8080);
|
||||
|
||||
// IPv6 Adresse
|
||||
$address = SocketAddress::ipv6('::1', 8080);
|
||||
|
||||
// Unix Domain Socket
|
||||
$address = SocketAddress::unix('/tmp/socket.sock');
|
||||
|
||||
// Von String parsen
|
||||
$address = SocketAddress::fromString('127.0.0.1:8080');
|
||||
$address = SocketAddress::fromString('/tmp/socket.sock');
|
||||
|
||||
// Eigenschaften
|
||||
$host = $address->getHost(); // null für Unix Sockets
|
||||
$port = $address->getPort(); // null für Unix Sockets
|
||||
$path = $address->getUnixPath(); // null für Network Sockets
|
||||
$protocol = $address->getProtocol(); // SocketProtocol Enum
|
||||
$isUnix = $address->isUnixSocket();
|
||||
$isNetwork = $address->isNetworkSocket();
|
||||
$string = $address->toString(); // "127.0.0.1:8080" oder "/tmp/socket.sock"
|
||||
```
|
||||
|
||||
### SocketType
|
||||
|
||||
Enum für Socket-Typen.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\ValueObjects\SocketType;
|
||||
|
||||
SocketType::TCP // SOCK_STREAM
|
||||
SocketType::UDP // SOCK_DGRAM
|
||||
SocketType::RAW // SOCK_RAW
|
||||
SocketType::RDM // SOCK_RDM
|
||||
SocketType::SEQPACKET // SOCK_SEQPACKET
|
||||
|
||||
// Methoden
|
||||
$type->getName(); // "TCP", "UDP", etc.
|
||||
$type->getValue(); // SOCK_STREAM, SOCK_DGRAM, etc.
|
||||
$type->isReliable(); // true für TCP, SEQPACKET
|
||||
$type->isConnectionless(); // true für UDP
|
||||
```
|
||||
|
||||
### SocketProtocol
|
||||
|
||||
Enum für Protokoll-Familien.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\ValueObjects\SocketProtocol;
|
||||
|
||||
SocketProtocol::IPv4 // AF_INET
|
||||
SocketProtocol::IPv6 // AF_INET6
|
||||
SocketProtocol::UNIX // AF_UNIX
|
||||
|
||||
// Methoden
|
||||
$protocol->getName(); // "IPv4", "IPv6", "Unix Domain"
|
||||
$protocol->getValue(); // AF_INET, AF_INET6, AF_UNIX
|
||||
$protocol->isNetwork(); // true für IPv4/IPv6
|
||||
$protocol->isUnix(); // true für Unix Domain
|
||||
```
|
||||
|
||||
### SocketOption
|
||||
|
||||
Enum für Socket-Optionen.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\ValueObjects\SocketOption;
|
||||
|
||||
SocketOption::SO_REUSEADDR
|
||||
SocketOption::SO_REUSEPORT
|
||||
SocketOption::SO_KEEPALIVE
|
||||
SocketOption::SO_LINGER
|
||||
SocketOption::SO_RCVBUF
|
||||
SocketOption::SO_SNDBUF
|
||||
SocketOption::SO_RCVTIMEO
|
||||
SocketOption::SO_SNDTIMEO
|
||||
SocketOption::TCP_NODELAY
|
||||
SocketOption::TCP_KEEPIDLE
|
||||
SocketOption::TCP_KEEPINTVL
|
||||
SocketOption::TCP_KEEPCNT
|
||||
|
||||
// Methoden
|
||||
$option->getName(); // "SO_REUSEADDR", etc.
|
||||
$option->getLevel(); // SOL_SOCKET oder SOL_TCP
|
||||
$option->getValue(); // SO_REUSEADDR, etc.
|
||||
```
|
||||
|
||||
### SocketResource
|
||||
|
||||
Type-safe Wrapper für Socket-Ressourcen mit automatischem Cleanup.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\ValueObjects\SocketResource;
|
||||
|
||||
$resource = SocketResource::fromResource($socket, $type, $protocol);
|
||||
|
||||
// Eigenschaften
|
||||
$socketResource = $resource->getResource(); // Native Socket-Resource
|
||||
$type = $resource->getType(); // SocketType
|
||||
$protocol = $resource->getProtocol(); // SocketProtocol
|
||||
$isClosed = $resource->isClosed();
|
||||
|
||||
// Manuelles Schließen (automatisch im Destructor)
|
||||
$resource->close();
|
||||
```
|
||||
|
||||
## Core Services
|
||||
|
||||
### SocketFactory
|
||||
|
||||
Factory für Socket-Erstellung mit automatischem Option-Setup.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketFactory;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
use App\Framework\Sockets\ValueObjects\SocketType;
|
||||
use App\Framework\Sockets\ValueObjects\SocketProtocol;
|
||||
|
||||
$factory = new SocketFactory();
|
||||
|
||||
// TCP Socket erstellen
|
||||
$address = SocketAddress::ipv4('127.0.0.1', 8080);
|
||||
$socket = $factory->createTcp($address);
|
||||
|
||||
// UDP Socket erstellen
|
||||
$socket = $factory->createUdp($address);
|
||||
|
||||
// Unix Domain Socket erstellen
|
||||
$unixAddress = SocketAddress::unix('/tmp/socket.sock');
|
||||
$socket = $factory->createUnix($unixAddress);
|
||||
|
||||
// Generische Erstellung
|
||||
$socket = $factory->createServer(SocketType::TCP, SocketProtocol::IPv4);
|
||||
$socket = $factory->createClient(SocketType::TCP, SocketProtocol::IPv4);
|
||||
```
|
||||
|
||||
### SocketServer
|
||||
|
||||
Server-Operationen für Socket-Verwaltung.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketServer;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
use App\Framework\Sockets\ValueObjects\SocketResource;
|
||||
|
||||
$server = new SocketServer();
|
||||
$address = SocketAddress::ipv4('0.0.0.0', 8080);
|
||||
|
||||
// Socket binden
|
||||
$server->bind($socket, $address);
|
||||
|
||||
// Auf Verbindungen lauschen
|
||||
$server->listen($socket, 10); // backlog = 10
|
||||
|
||||
// Verbindung akzeptieren (non-blocking)
|
||||
$connection = $server->accept($socket);
|
||||
if ($connection !== null) {
|
||||
// Verbindung verarbeiten
|
||||
$clientSocket = $connection->getSocket();
|
||||
$clientAddress = $connection->getAddress();
|
||||
}
|
||||
|
||||
// Socket-Auswahl (select)
|
||||
$read = [$socket];
|
||||
$write = [];
|
||||
$except = [];
|
||||
$numReady = $server->select($read, $write, $except, 1); // 1 Sekunde Timeout
|
||||
|
||||
// Non-blocking/Blocking Mode
|
||||
$server->setNonBlocking($socket);
|
||||
$server->setBlocking($socket);
|
||||
```
|
||||
|
||||
### SocketClient
|
||||
|
||||
Client-Operationen für Socket-Verbindungen.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketClient;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
use App\Framework\Sockets\ValueObjects\SocketResource;
|
||||
|
||||
$client = new SocketClient();
|
||||
$address = SocketAddress::ipv4('127.0.0.1', 8080);
|
||||
|
||||
// Verbindung herstellen
|
||||
$client->connect($socket, $address);
|
||||
|
||||
// Daten lesen
|
||||
$data = $client->read($socket, 1024); // null wenn keine Daten (non-blocking)
|
||||
|
||||
// Daten schreiben
|
||||
$bytesWritten = $client->write($socket, "Hello, World!");
|
||||
|
||||
// Daten senden (mit Flags)
|
||||
$bytesSent = $client->send($socket, $data, MSG_DONTWAIT);
|
||||
|
||||
// Daten empfangen
|
||||
$data = $client->receive($socket, 1024, MSG_DONTWAIT);
|
||||
|
||||
// Verbindung schließen
|
||||
$client->close($socket);
|
||||
```
|
||||
|
||||
### SocketConnection
|
||||
|
||||
Wrapper für Socket-Verbindungen mit Metadaten.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketConnection;
|
||||
|
||||
$connection = SocketConnection::create($socketResource, $address);
|
||||
|
||||
// Eigenschaften
|
||||
$socket = $connection->getSocket();
|
||||
$address = $connection->getAddress();
|
||||
$connectionId = $connection->getConnectionId();
|
||||
$isClosed = $connection->isClosed();
|
||||
|
||||
// Verbindung schließen
|
||||
$connection->close();
|
||||
```
|
||||
|
||||
### SocketConnectionPool
|
||||
|
||||
Verwaltung mehrerer Socket-Verbindungen.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketConnectionPool;
|
||||
|
||||
$pool = new SocketConnectionPool(
|
||||
maxConnectionsPerAddress: 5,
|
||||
connectionTimeoutSeconds: 300
|
||||
);
|
||||
|
||||
// Verbindung hinzufügen
|
||||
$pool->add($connection);
|
||||
|
||||
// Verbindung entfernen
|
||||
$pool->remove($connection);
|
||||
|
||||
// Verbindung abrufen
|
||||
$connection = $pool->get($connectionId);
|
||||
$connections = $pool->getConnectionsByAddress($address);
|
||||
$connection = $pool->getConnectionForAddress($address);
|
||||
|
||||
// Dead Connections bereinigen
|
||||
$removed = $pool->cleanupDeadConnections();
|
||||
|
||||
// Statistiken
|
||||
$count = $pool->getConnectionCount();
|
||||
$allConnections = $pool->getAllConnections();
|
||||
```
|
||||
|
||||
### SocketService
|
||||
|
||||
Haupt-Facade für alle Socket-Operationen.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketService;
|
||||
|
||||
$socketService = $container->get(SocketService::class);
|
||||
|
||||
// Socket-Erstellung
|
||||
$socket = $socketService->createTcp($address);
|
||||
$socket = $socketService->createUdp($address);
|
||||
$socket = $socketService->createUnix($address);
|
||||
|
||||
// Server-Operationen
|
||||
$socketService->bind($socket, $address);
|
||||
$socketService->listen($socket, 10);
|
||||
$connection = $socketService->accept($socket);
|
||||
$socketService->setNonBlocking($socket);
|
||||
|
||||
// Client-Operationen
|
||||
$socketService->connect($socket, $address);
|
||||
$data = $socketService->read($socket, 1024);
|
||||
$bytes = $socketService->write($socket, $data);
|
||||
|
||||
// Connection Pool
|
||||
$socketService->addConnection($connection);
|
||||
$socketService->removeConnection($connection);
|
||||
$connection = $socketService->getConnection($connectionId);
|
||||
$removed = $socketService->cleanupDeadConnections();
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
### PCNTL Integration
|
||||
|
||||
Socket-Server in geforkten Worker-Prozessen für Load Balancing.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\Integration\PcntlSocketServer;
|
||||
use App\Framework\Sockets\SocketService;
|
||||
use App\Framework\Pcntl\PcntlService;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
use App\Framework\Sockets\ValueObjects\SocketResource;
|
||||
|
||||
$socketService = $container->get(SocketService::class);
|
||||
$pcntlService = $container->get(PcntlService::class);
|
||||
|
||||
$pcntlServer = new PcntlSocketServer(
|
||||
socketService: $socketService,
|
||||
pcntlService: $pcntlService,
|
||||
maxWorkers: 4
|
||||
);
|
||||
|
||||
// Connection Handler setzen
|
||||
$pcntlServer->setConnectionHandler(function (SocketConnection $connection) {
|
||||
// Verbindung verarbeiten
|
||||
$socket = $connection->getSocket();
|
||||
$data = $socketService->read($socket->getResource(), 1024);
|
||||
// ...
|
||||
});
|
||||
|
||||
// Server starten
|
||||
$address = SocketAddress::ipv4('0.0.0.0', 8080);
|
||||
$socket = $socketService->createTcp($address);
|
||||
$pcntlServer->start($socket, $address, workerCount: 4);
|
||||
|
||||
// Server stoppen (graceful shutdown)
|
||||
$pcntlServer->stop();
|
||||
```
|
||||
|
||||
### Async Integration
|
||||
|
||||
Non-blocking Socket-Server mit Fibers für parallele Connection-Verarbeitung.
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\Integration\AsyncSocketServer;
|
||||
use App\Framework\Sockets\SocketService;
|
||||
use App\Framework\Async\FiberManager;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
|
||||
$socketService = $container->get(SocketService::class);
|
||||
$fiberManager = $container->get(FiberManager::class);
|
||||
|
||||
$asyncServer = new AsyncSocketServer(
|
||||
socketService: $socketService,
|
||||
fiberManager: $fiberManager
|
||||
);
|
||||
|
||||
// Server starten (gibt Fiber zurück)
|
||||
$address = SocketAddress::ipv4('0.0.0.0', 8080);
|
||||
$socket = $socketService->createTcp($address);
|
||||
|
||||
$serverFiber = $asyncServer->startAsync(
|
||||
socket: $socket,
|
||||
address: $address,
|
||||
connectionHandler: function (SocketConnection $connection) {
|
||||
// Connection in separatem Fiber verarbeiten
|
||||
$socket = $connection->getSocket();
|
||||
|
||||
// Non-blocking lesen
|
||||
$readFiber = $asyncServer->readAsync($connection, 1024);
|
||||
$data = $readFiber->start();
|
||||
|
||||
// Non-blocking schreiben
|
||||
$writeFiber = $asyncServer->writeAsync($connection, "Response");
|
||||
$bytes = $writeFiber->start();
|
||||
}
|
||||
);
|
||||
|
||||
// Server-Fiber starten
|
||||
$serverFiber->start();
|
||||
```
|
||||
|
||||
## Beispiele
|
||||
|
||||
### Einfacher TCP Server
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketService;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
|
||||
$socketService = $container->get(SocketService::class);
|
||||
$address = SocketAddress::ipv4('0.0.0.0', 8080);
|
||||
|
||||
// Socket erstellen
|
||||
$socket = $socketService->createTcp($address);
|
||||
|
||||
// Binden und lauschen
|
||||
$socketService->bind($socket, $address);
|
||||
$socketService->listen($socket, 10);
|
||||
$socketService->setNonBlocking($socket);
|
||||
|
||||
// Hauptschleife
|
||||
while (true) {
|
||||
// Verbindung akzeptieren
|
||||
$connection = $socketService->accept($socket);
|
||||
|
||||
if ($connection !== null) {
|
||||
$clientSocket = $connection->getSocket();
|
||||
|
||||
// Daten lesen
|
||||
$data = $socketService->read($clientSocket, 1024);
|
||||
|
||||
if ($data !== null) {
|
||||
// Antwort senden
|
||||
$socketService->write($clientSocket, "Echo: " . $data);
|
||||
}
|
||||
|
||||
// Verbindung schließen
|
||||
$connection->close();
|
||||
}
|
||||
|
||||
usleep(10000); // 10ms
|
||||
}
|
||||
```
|
||||
|
||||
### TCP Client
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketService;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
|
||||
$socketService = $container->get(SocketService::class);
|
||||
$address = SocketAddress::ipv4('127.0.0.1', 8080);
|
||||
|
||||
// Client-Socket erstellen
|
||||
$socket = $socketService->createClient(
|
||||
SocketType::TCP,
|
||||
SocketProtocol::IPv4
|
||||
);
|
||||
|
||||
// Verbinden
|
||||
$socketService->connect($socket, $address);
|
||||
|
||||
// Daten senden
|
||||
$socketService->write($socket, "Hello, Server!");
|
||||
|
||||
// Antwort lesen
|
||||
$response = $socketService->read($socket, 1024);
|
||||
|
||||
// Verbindung schließen
|
||||
$socketService->close($socket);
|
||||
```
|
||||
|
||||
### Unix Domain Socket Server
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketService;
|
||||
use App\Framework\Sockets\ValueObjects\SocketAddress;
|
||||
|
||||
$socketService = $container->get(SocketService::class);
|
||||
$address = SocketAddress::unix('/tmp/mysocket.sock');
|
||||
|
||||
// Socket erstellen
|
||||
$socket = $socketService->createUnix($address);
|
||||
|
||||
// Binden und lauschen
|
||||
$socketService->bind($socket, $address);
|
||||
$socketService->listen($socket, 10);
|
||||
|
||||
// Verbindungen akzeptieren
|
||||
while (true) {
|
||||
$connection = $socketService->accept($socket);
|
||||
|
||||
if ($connection !== null) {
|
||||
// Verarbeitung
|
||||
$socket = $connection->getSocket();
|
||||
$data = $socketService->read($socket, 1024);
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Migration von bestehendem Code
|
||||
|
||||
### Von `socket_create()` zu `SocketFactory`
|
||||
|
||||
**Vorher:**
|
||||
```php
|
||||
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```php
|
||||
$address = SocketAddress::ipv4('127.0.0.1', 8080);
|
||||
$socket = $socketService->createTcp($address);
|
||||
```
|
||||
|
||||
### Von `socket_bind()`/`socket_listen()` zu `SocketServer`
|
||||
|
||||
**Vorher:**
|
||||
```php
|
||||
socket_bind($socket, '127.0.0.1', 8080);
|
||||
socket_listen($socket, 10);
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```php
|
||||
$address = SocketAddress::ipv4('127.0.0.1', 8080);
|
||||
$socketService->bind($socket, $address);
|
||||
$socketService->listen($socket, 10);
|
||||
```
|
||||
|
||||
### Von `socket_accept()` zu `SocketServer::accept()`
|
||||
|
||||
**Vorher:**
|
||||
```php
|
||||
$clientSocket = socket_accept($socket);
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```php
|
||||
$connection = $socketService->accept($socket);
|
||||
if ($connection !== null) {
|
||||
$clientSocket = $connection->getSocket();
|
||||
}
|
||||
```
|
||||
|
||||
### Von `socket_select()` zu `SocketServer::select()`
|
||||
|
||||
**Vorher:**
|
||||
```php
|
||||
socket_select($read, $write, $except, 1);
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```php
|
||||
$numReady = $socketService->select($read, $write, $except, 1);
|
||||
```
|
||||
|
||||
### Von `socket_read()`/`socket_write()` zu `SocketClient`
|
||||
|
||||
**Vorher:**
|
||||
```php
|
||||
$data = socket_read($socket, 1024);
|
||||
socket_write($socket, $data, strlen($data));
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```php
|
||||
$data = $socketService->read($socket, 1024);
|
||||
$bytes = $socketService->write($socket, $data);
|
||||
```
|
||||
|
||||
### Von `socket_close()` zu `SocketResource` Destructor
|
||||
|
||||
**Vorher:**
|
||||
```php
|
||||
socket_close($socket);
|
||||
```
|
||||
|
||||
**Nachher:**
|
||||
```php
|
||||
// Automatisch im Destructor, oder manuell:
|
||||
$socket->close();
|
||||
```
|
||||
|
||||
## Fehlerbehandlung
|
||||
|
||||
Das Sockets-Modul verwendet spezifische Exceptions für verschiedene Fehlertypen:
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\Exceptions\SocketException;
|
||||
use App\Framework\Sockets\Exceptions\SocketBindException;
|
||||
use App\Framework\Sockets\Exceptions\SocketConnectException;
|
||||
use App\Framework\Sockets\Exceptions\SocketReadException;
|
||||
|
||||
try {
|
||||
$socketService->bind($socket, $address);
|
||||
} catch (SocketBindException $e) {
|
||||
// Bind-Fehler behandeln
|
||||
error_log("Failed to bind: " . $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
$socketService->connect($socket, $address);
|
||||
} catch (SocketConnectException $e) {
|
||||
// Connect-Fehler behandeln
|
||||
error_log("Failed to connect: " . $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $socketService->read($socket, 1024);
|
||||
} catch (SocketReadException $e) {
|
||||
// Read-Fehler behandeln
|
||||
if ($e->getMessage() === 'Socket connection closed') {
|
||||
// Verbindung geschlossen
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **SocketService verwenden**: Nutze immer `SocketService` als Haupt-API statt direkter Aufrufe der einzelnen Services
|
||||
2. **Value Objects**: Verwende immer Value Objects (`SocketAddress`, `SocketType`, etc.) statt primitiver Typen
|
||||
3. **Resource Management**: `SocketResource` kümmert sich automatisch um Cleanup, aber manuelles Schließen ist für explizite Kontrolle möglich
|
||||
4. **Non-blocking I/O**: Verwende `setNonBlocking()` für Server-Sockets, um mehrere Verbindungen parallel zu handhaben
|
||||
5. **Connection Pooling**: Nutze `SocketConnectionPool` für Client-Verbindungen, die wiederverwendet werden sollen
|
||||
6. **Error Handling**: Fange spezifische Exceptions (`SocketBindException`, `SocketConnectException`, etc.) für präzise Fehlerbehandlung
|
||||
7. **PCNTL für Load Balancing**: Verwende `PcntlSocketServer` für Socket-Server, die mehrere Worker-Prozesse benötigen
|
||||
8. **Async für Parallelität**: Verwende `AsyncSocketServer` für non-blocking I/O mit Fibers
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
Alle Services werden automatisch über `SocketsInitializer` im DI-Container registriert:
|
||||
|
||||
```php
|
||||
use App\Framework\Sockets\SocketService;
|
||||
use App\Framework\Sockets\SocketFactory;
|
||||
use App\Framework\Sockets\SocketServer;
|
||||
use App\Framework\Sockets\SocketClient;
|
||||
|
||||
// Services sind im Container verfügbar
|
||||
$socketService = $container->get(SocketService::class);
|
||||
$factory = $container->get(SocketFactory::class);
|
||||
$server = $container->get(SocketServer::class);
|
||||
$client = $container->get(SocketClient::class);
|
||||
```
|
||||
|
||||
## Framework-Kompatibilität
|
||||
|
||||
Das Sockets-Modul folgt allen Framework-Prinzipien:
|
||||
|
||||
- ✅ **Final readonly classes** wo möglich
|
||||
- ✅ **Value Objects** statt Primitiven
|
||||
- ✅ **Dependency Injection** überall
|
||||
- ✅ **Composition** statt Inheritance
|
||||
- ✅ **Strict Types** (`declare(strict_types=1)`)
|
||||
- ✅ **PSR-12** Code Style
|
||||
|
||||
## Siehe auch
|
||||
|
||||
- [PCNTL Module Documentation](./pcntl-module.md) - Für PCNTL-Integration
|
||||
- [Async Module Documentation](./async-module.md) - Für Async-Integration
|
||||
- [Framework Guidelines](./guidelines.md) - Allgemeine Framework-Prinzipien
|
||||
|
||||
1141
docs/claude/typed-string-system.md
Normal file
1141
docs/claude/typed-string-system.md
Normal file
File diff suppressed because it is too large
Load Diff
26
docs/codex/guidelines.md
Normal file
26
docs/codex/guidelines.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Codex Agent Guidelines
|
||||
|
||||
This document supplements `AGENTS.md` with Codex-specific working notes. Always treat `AGENTS.md` as the source of truth and keep this file aligned with future updates.
|
||||
|
||||
## Core Expectations
|
||||
|
||||
- Obtain explicit approval from the project owner before attempting any code change or file edit.
|
||||
- Follow every checklist item in `AGENTS.md`, including framework principles, testing strategy, and tooling conventions.
|
||||
- Keep documentation edits consistent with project tone (PSR-12 code examples, ASCII character set, precise terminology).
|
||||
|
||||
## Workflow Notes
|
||||
|
||||
- Prefer small, reviewable steps and clearly report what changed, including file paths and line references.
|
||||
- When a task involves multiple steps, draft a lightweight plan unless the work is trivial.
|
||||
- Record unanswered questions or ambiguities in the final message so the owner can respond quickly.
|
||||
|
||||
## Tooling Reminders
|
||||
|
||||
- Use `bash -lc` shell invocations with explicit `workdir` as required by the Codex CLI harness.
|
||||
- Rely on `rg` for searches; fall back to alternatives only if `rg` is unavailable.
|
||||
- Do not run container or network commands without explicit permission when sandboxed.
|
||||
|
||||
## Documentation Maintenance
|
||||
|
||||
- Update this file whenever Codex-specific guidance changes so it remains synchronized with the rest of the ecosystem docs (e.g., keep parity with `docs/claude/` where it makes sense).
|
||||
- Coordinate with the project owner before introducing new sections or workflows that impact other agents.
|
||||
@@ -204,13 +204,13 @@ Roave automatically blocks `composer install` or `composer update` if any instal
|
||||
Current project uses **PHP 8.5 RC3** (bleeding edge), which causes dependency resolution conflicts:
|
||||
```
|
||||
brianium/paratest v7.8.4 requires php ~8.2.0 || ~8.3.0 || ~8.4.0
|
||||
your php version (8.5.0RC3) does not satisfy that requirement
|
||||
your php version (8.5.0RC4) does not satisfy that requirement
|
||||
```
|
||||
|
||||
**Planned Integration:** When PHP 8.5 stable is released and all testing dependencies support it.
|
||||
|
||||
> ℹ️ **PHP Runtime Strategy:**
|
||||
> - Runtime container builds accept `--build-arg PHP_VERSION` (default `8.5.0RC3`) to keep PHP aligned with upstream RC tags.
|
||||
> - Runtime container builds accept `--build-arg PHP_VERSION` (default `8.5.0RC4`) to keep PHP aligned with upstream RC tags.
|
||||
> - `.gitea/workflows/production-deploy.yml` sets the same version for CI rebuilds (`--pull` ensures fresh layers).
|
||||
> - We'll move to `8.5.0RC4` as soon as upstream publishes the image and switch to the latest stable PHP release at the end of November.
|
||||
|
||||
|
||||
22
docs/deployment/DEPLOYMENT_FIX.md
Normal file
22
docs/deployment/DEPLOYMENT_FIX.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Deployment Fix f?r Staging 502 Error
|
||||
|
||||
Das Problem: Nach jedem Deployment tritt der 502-Fehler wieder auf.
|
||||
|
||||
## L?sung: Deployment-Script erweitern
|
||||
|
||||
F?ge nach Zeile 991 in `.gitea/workflows/build-image.yml` folgenden Code ein:
|
||||
|
||||
```yaml
|
||||
# Fix nginx upstream configuration - critical fix for 502 errors
|
||||
# sites-available/default uses 127.0.0.1:9000 but PHP-FPM runs in staging-app container
|
||||
echo \"?? Fixing nginx PHP-FPM upstream configuration (post-deploy fix)...\"
|
||||
sleep 5
|
||||
docker compose exec -T staging-nginx sed -i '/upstream php-upstream {/,/}/s|server 127.0.0.1:9000;|server staging-app:9000;|g' /etc/nginx/sites-available/default || echo \"?? Upstream fix (127.0.0.1) failed\"
|
||||
docker compose exec -T staging-nginx sed -i '/upstream php-upstream {/,/}/s|server localhost:9000;|server staging-app:9000;|g' /etc/nginx/sites-available/default || echo \"?? Upstream fix (localhost) failed\"
|
||||
docker compose exec -T staging-nginx nginx -t && docker compose restart staging-nginx || echo \"?? Nginx config test or restart failed\"
|
||||
echo \"? Nginx configuration fixed and reloaded\"
|
||||
```
|
||||
|
||||
**Position:** Nach Zeile 991 (`docker compose restart staging-app || echo \"?? Failed to restart staging-app\"`) und vor Zeile 993 (`echo \"? Waiting for services to stabilize...\"`)
|
||||
|
||||
**Grund:** Die Container werden mit `--force-recreate` neu erstellt, wodurch die Datei `/etc/nginx/sites-available/default` wieder aus dem Docker-Image kommt und `127.0.0.1:9000` verwendet. Dieser Fix muss nach jedem Deployment ausgef?hrt werden.
|
||||
266
docs/deployment/ENV_SETUP.md
Normal file
266
docs/deployment/ENV_SETUP.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# Environment Configuration Guide
|
||||
|
||||
## 📁 .env File Structure (Base + Override Pattern)
|
||||
|
||||
Die neue Struktur verwendet ein **Base + Override Pattern** (analog zu docker-compose):
|
||||
|
||||
```
|
||||
├── .env.example # Template für neue Entwickler (vollständige Dokumentation)
|
||||
├── .env.base # Gemeinsame Variablen für alle Environments (versioniert)
|
||||
├── .env.local # Lokale Development-Overrides (gitignored)
|
||||
├── .env.staging # Staging-spezifische Overrides (optional, gitignored)
|
||||
└── .env.production # Production (generiert durch Ansible, nicht im Repo)
|
||||
```
|
||||
|
||||
## 🏗️ Development Setup
|
||||
|
||||
### Initial Setup
|
||||
|
||||
```bash
|
||||
# 1. .env.base ist bereits im Repository (gemeinsame Variablen)
|
||||
# 2. Erstelle .env.local für lokale Overrides
|
||||
cp .env.example .env.local
|
||||
|
||||
# 3. Passe .env.local an deine lokale Entwicklung an
|
||||
# - DB Credentials (lokal)
|
||||
# - API Keys (lokal)
|
||||
# - Debug-Flags
|
||||
```
|
||||
|
||||
### Framework lädt automatisch: `.env.base` → `.env.local` (Overrides)
|
||||
|
||||
**Priorität:**
|
||||
1. System Environment Variables (Docker ENV)
|
||||
2. `.env.base` (gemeinsame Basis)
|
||||
3. `.env.local` (lokale Overrides)
|
||||
4. `.env.secrets` (verschlüsselte Secrets, optional)
|
||||
|
||||
**Wichtig:** `env_file` in Docker Compose ist nicht nötig!
|
||||
- Framework lädt automatisch `.env.base` → `.env.local` via `EncryptedEnvLoader`
|
||||
- Docker Compose `env_file` ist optional und wird nur für Container-interne Variablen benötigt
|
||||
- PHP-Anwendung lädt ENV-Variablen direkt aus den Dateien
|
||||
|
||||
**Backward Compatibility:**
|
||||
- Falls `.env.base` oder `.env.local` nicht existieren, wird `.env` geladen (Fallback)
|
||||
- Migration: Bestehende `.env` Files funktionieren weiterhin
|
||||
|
||||
## 🚀 Production Deployment
|
||||
|
||||
### Production .env Management
|
||||
|
||||
**WICHTIG**: Production `.env` wird **NICHT** aus dem Repository deployed!
|
||||
|
||||
### Single Source of Truth
|
||||
|
||||
```
|
||||
Server: /home/deploy/michaelschiemer/shared/.env.production
|
||||
```
|
||||
|
||||
Dieser File wird von **Ansible** automatisch erstellt aus:
|
||||
```
|
||||
deployment/infrastructure/templates/.env.production.j2
|
||||
```
|
||||
|
||||
### Production Deployment Process
|
||||
|
||||
```bash
|
||||
# Ansible Playbook erstellt automatisch die Production .env
|
||||
cd deployment/infrastructure
|
||||
ansible-playbook -i inventories/production/hosts.yml \
|
||||
playbooks/deploy-rsync-based.yml \
|
||||
--vault-password-file .vault_pass
|
||||
```
|
||||
|
||||
**Ansible macht dabei**:
|
||||
1. Template `.env.production.j2` rendern
|
||||
2. Vault-verschlüsselte Secrets einsetzen
|
||||
3. File nach `/home/deploy/michaelschiemer/shared/.env.production` deployen
|
||||
4. Docker Compose mounted diesen File in Container
|
||||
|
||||
## 🔒 Security & Secret Management
|
||||
|
||||
### Docker Secrets (Production & Staging)
|
||||
|
||||
**Production und Staging verwenden Docker Secrets:**
|
||||
|
||||
1. **Ansible Vault → Docker Secrets Dateien**
|
||||
- Ansible Playbook erstellt Secret-Dateien in `secrets/` Verzeichnis
|
||||
- Dateien haben sichere Permissions (0600)
|
||||
|
||||
2. **Docker Compose Secrets**
|
||||
- Secrets werden in `docker-compose.base.yml` definiert
|
||||
- Environment-Variablen nutzen `*_FILE` Pattern (z.B. `DB_PASSWORD_FILE=/run/secrets/db_user_password`)
|
||||
- Framework lädt automatisch via `DockerSecretsResolver`
|
||||
|
||||
3. **Framework Support**
|
||||
- `DockerSecretsResolver` unterstützt automatisch `*_FILE` Pattern
|
||||
- Kein manuelles Secret-Loading mehr nötig (wird automatisch vom Framework behandelt)
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# .env.local niemals committen
|
||||
git status
|
||||
# Should show: .env.local (untracked) ✅
|
||||
|
||||
# .env.base ist versioniert (keine Secrets!)
|
||||
# Falls versehentlich staged:
|
||||
git reset HEAD .env.local
|
||||
```
|
||||
|
||||
### Production
|
||||
|
||||
- ✅ Secrets in Ansible Vault
|
||||
- ✅ Ansible erstellt Docker Secrets Dateien (`secrets/*.txt`)
|
||||
- ✅ Docker Compose Secrets aktiviert
|
||||
- ✅ Framework lädt automatisch via `*_FILE` Pattern
|
||||
- ✅ .env.production auf Server wird NICHT ins Repository committed
|
||||
- ✅ Template `application.env.j2` verwendet `*_FILE` Pattern
|
||||
|
||||
## 📝 Adding New Environment Variables
|
||||
|
||||
### Development
|
||||
|
||||
```bash
|
||||
# 1. Add to .env.base if shared across environments
|
||||
echo "NEW_API_KEY=" >> .env.base
|
||||
|
||||
# 2. Add to .env.local for local development
|
||||
echo "NEW_API_KEY=abc123..." >> .env.local
|
||||
|
||||
# 3. Update .env.example for documentation
|
||||
echo "NEW_API_KEY=your_api_key_here" >> .env.example
|
||||
```
|
||||
|
||||
**Hinweis:** Wenn die Variable nur für lokale Entwicklung ist, nur in `.env.local` hinzufügen.
|
||||
|
||||
### Production (mit Docker Secrets)
|
||||
|
||||
```bash
|
||||
# 1. Add to Ansible Template (use *_FILE pattern for secrets)
|
||||
# File: deployment/ansible/templates/application.env.j2
|
||||
echo "# Use Docker Secrets via *_FILE pattern" >> application.env.j2
|
||||
echo "NEW_API_KEY_FILE=/run/secrets/new_api_key" >> application.env.j2
|
||||
|
||||
# 2. Add to docker-compose.base.yml secrets section
|
||||
# File: docker-compose.base.yml
|
||||
# secrets:
|
||||
# new_api_key:
|
||||
# file: ./secrets/new_api_key.txt
|
||||
|
||||
# 3. Add secret to Ansible Vault
|
||||
ansible-vault edit deployment/ansible/secrets/production.vault.yml
|
||||
# Add: vault_new_api_key: "production_value"
|
||||
|
||||
# 4. Update setup-production-secrets.yml to create secret file
|
||||
# File: deployment/ansible/playbooks/setup-production-secrets.yml
|
||||
# Add to loop:
|
||||
# - name: new_api_key
|
||||
# value: "{{ vault_new_api_key }}"
|
||||
|
||||
# 5. Deploy
|
||||
cd deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml \
|
||||
playbooks/setup-production-secrets.yml \
|
||||
--vault-password-file .vault_pass
|
||||
```
|
||||
|
||||
### Production (ohne Docker Secrets, fallback)
|
||||
|
||||
Falls Docker Secrets nicht verwendet werden sollen:
|
||||
|
||||
```bash
|
||||
# 1. Add to Ansible Template
|
||||
# File: deployment/ansible/templates/application.env.j2
|
||||
echo "NEW_API_KEY={{ vault_new_api_key }}" >> application.env.j2
|
||||
|
||||
# 2. Add secret to Ansible Vault
|
||||
ansible-vault edit deployment/ansible/secrets/production.vault.yml
|
||||
# Add: vault_new_api_key: "production_value"
|
||||
|
||||
# 3. Deploy
|
||||
cd deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml \
|
||||
playbooks/deploy-update.yml
|
||||
```
|
||||
|
||||
## 🗑️ Removed Files (Consolidation 27.10.2024)
|
||||
|
||||
Diese Files wurden gelöscht, da sie redundant/nicht verwendet wurden:
|
||||
|
||||
```
|
||||
❌ .env.production (Root - redundant)
|
||||
❌ .env.production.example (Root - nicht verwendet)
|
||||
❌ .env.backup.20250912_133135 (Altes Backup)
|
||||
❌ .env.analytics.example (In .env.example integriert)
|
||||
❌ .env.secrets.example (In .env.example integriert)
|
||||
❌ deployment/applications/environments/ (Gesamtes Directory gelöscht)
|
||||
```
|
||||
|
||||
## ✅ Current State
|
||||
|
||||
### Local Development
|
||||
- ✅ Base File: `.env.base` (versioniert, gemeinsame Variablen)
|
||||
- ✅ Override File: `.env.local` (gitignored, lokale Anpassungen)
|
||||
- ✅ Template: `.env.example` (Dokumentation)
|
||||
- ✅ Framework lädt automatisch: `.env.base` → `.env.local` (Overrides)
|
||||
|
||||
### Production
|
||||
- ✅ Single Source: `/home/deploy/michaelschiemer/shared/.env.production` (auf Server)
|
||||
- ✅ Verwaltet durch: Ansible Template `application.env.j2`
|
||||
- ✅ Secrets: Docker Secrets (`secrets/*.txt` Dateien)
|
||||
- ✅ Framework lädt automatisch via `*_FILE` Pattern (`DockerSecretsResolver`)
|
||||
- ✅ Keine Duplikate
|
||||
|
||||
### Staging
|
||||
- ✅ Docker Compose Environment Variables
|
||||
- ✅ Docker Secrets aktiviert (wie Production)
|
||||
- ✅ Optional: `.env.staging` für Staging-spezifische Overrides
|
||||
|
||||
## 🔍 Verification
|
||||
|
||||
```bash
|
||||
# Check local .env files
|
||||
ls -la .env*
|
||||
# Should show: .env.base (versioniert), .env.local (gitignored), .env.example
|
||||
|
||||
# Check Ansible template exists
|
||||
ls -la deployment/ansible/templates/application.env.j2
|
||||
# Should exist
|
||||
|
||||
# Check Docker Secrets files exist (on server)
|
||||
ls -la {{ app_stack_path }}/secrets/
|
||||
# Should show: db_user_password.txt, redis_password.txt, app_key.txt, etc.
|
||||
|
||||
# Check NO old files remain
|
||||
find . -name ".env.production" -o -name ".env.*.example" | grep -v .env.example | grep -v .env.base
|
||||
# Should be empty
|
||||
```
|
||||
|
||||
## 📞 Support
|
||||
|
||||
Bei Fragen zum .env Setup:
|
||||
- Development: Siehe `.env.base` (gemeinsame Variablen) und `.env.example` (Dokumentation)
|
||||
- Production: Siehe `deployment/ansible/templates/application.env.j2`
|
||||
- Secrets: Docker Secrets aktiviert, verwaltet durch Ansible Vault
|
||||
- Migration: Framework unterstützt Fallback auf `.env` (alte Struktur)
|
||||
|
||||
## 🔄 Migration von alter Struktur
|
||||
|
||||
**Von `.env` zu `.env.base` + `.env.local`:**
|
||||
|
||||
```bash
|
||||
# 1. Erstelle .env.base (gemeinsame Variablen extrahieren)
|
||||
# (wird automatisch vom Framework erkannt)
|
||||
|
||||
# 2. Erstelle .env.local (nur lokale Overrides)
|
||||
cp .env .env.local
|
||||
|
||||
# 3. Entferne gemeinsame Variablen aus .env.local
|
||||
# (nur lokale Anpassungen behalten)
|
||||
|
||||
# 4. Alte .env kann später entfernt werden
|
||||
# (nach erfolgreicher Migration)
|
||||
```
|
||||
|
||||
**Hinweis:** Framework lädt automatisch `.env.base` + `.env.local`. Falls diese nicht existieren, wird `.env` als Fallback geladen (Backward Compatibility).
|
||||
100
docs/deployment/status/.deployment-test-status.md
Normal file
100
docs/deployment/status/.deployment-test-status.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Deployment-Test Status - Chat-Zusammenfassung
|
||||
|
||||
**Datum**: Aktuelle Session
|
||||
**Zweck**: Staging-Setup mit separaten Datenbank-Stacks testen
|
||||
|
||||
## ✅ Abgeschlossen
|
||||
|
||||
### 1. Implementierung
|
||||
- ✅ Separate PostgreSQL-Stacks erstellt (Production & Staging)
|
||||
- ✅ Ansible-Roles für PostgreSQL-Stacks
|
||||
- ✅ Application-Stacks angepasst (DB-Verbindungen)
|
||||
- ✅ Staging-Setup Playbook erstellt
|
||||
- ✅ Dokumentation aktualisiert
|
||||
- ✅ Quick-Start-Scripts erstellt
|
||||
- ✅ Ansible-Verifikations-Playbooks erstellt
|
||||
|
||||
### 2. Lokale Verifikation
|
||||
- ✅ PostgreSQL-Production Stack: Syntax OK
|
||||
- ✅ PostgreSQL-Staging Stack: Syntax OK
|
||||
- ✅ Staging Stack (Root): Syntax OK
|
||||
- ⚠️ Ansible: Nicht lokal installiert (wird auf Control-Node benötigt)
|
||||
|
||||
## ❌ Aktuelle Probleme
|
||||
|
||||
### SSH-Zugriff
|
||||
- **Problem**: SSH-Key kann nicht geladen werden
|
||||
- **Fehler**: `error in libcrypto` / `Permission denied (publickey)`
|
||||
- **Key vorhanden**: `~/.ssh/production` (Berechtigungen: 600)
|
||||
- **Mögliche Ursachen**: Key-Format, WSL/libcrypto-Kompatibilität
|
||||
|
||||
## 📋 Nächste Schritte
|
||||
|
||||
### Option 1: Manuelle Tests auf Server
|
||||
- **Anleitung**: `deployment/docs/guides/manual-server-test.md`
|
||||
- **Schritte**:
|
||||
1. SSH-Verbindung: `ssh production`
|
||||
2. PostgreSQL-Stacks starten
|
||||
3. Networks verifizieren
|
||||
4. Datenbank-Verbindungen testen
|
||||
5. Health-Checks durchführen
|
||||
|
||||
### Option 2: Ansible verwenden
|
||||
```bash
|
||||
cd ~/dev/michaelschiemer/deployment/ansible
|
||||
ansible-playbook -i inventory/production.yml playbooks/verify-staging.yml
|
||||
```
|
||||
|
||||
### Option 3: Quick-Start-Script (auf Server)
|
||||
```bash
|
||||
cd ~/deployment
|
||||
./scripts/staging-quick-start.sh
|
||||
```
|
||||
|
||||
## 📁 Wichtige Dateien
|
||||
|
||||
### Dokumentation
|
||||
- `deployment/docs/guides/test-execution-plan.md` - Detaillierter Testplan
|
||||
- `deployment/docs/guides/manual-server-test.md` - Manuelle Test-Anleitung
|
||||
- `deployment/docs/guides/ansible-vs-bash-scripts.md` - Tool-Vergleich
|
||||
|
||||
### Scripts
|
||||
- `deployment/scripts/staging-quick-start.sh` - Interaktives Test-Script
|
||||
- `deployment/scripts/production-quick-start.sh` - Production Test-Script
|
||||
|
||||
### Ansible-Playbooks
|
||||
- `deployment/ansible/playbooks/setup-staging.yml` - Staging-Setup
|
||||
- `deployment/ansible/playbooks/verify-staging.yml` - Staging-Verifikation
|
||||
- `deployment/ansible/playbooks/verify-production.yml` - Production-Verifikation
|
||||
|
||||
## 🎯 Test-Phasen (noch ausstehend)
|
||||
|
||||
1. ✅ Phase 1: Lokale Syntax-Verifikation
|
||||
2. ⏳ Phase 2: PostgreSQL-Stacks testen (auf Server)
|
||||
3. ⏳ Phase 3: Networks verifizieren
|
||||
4. ⏳ Phase 4: Ansible-Setup testen
|
||||
5. ⏳ Phase 5: Datenbank-Verbindungen testen
|
||||
6. ⏳ Phase 6: Health-Checks
|
||||
7. ⏳ Phase 7: CI/CD-Workflow testen
|
||||
8. ⏳ Phase 8: Datenbank-Isolation testen
|
||||
|
||||
## 💡 Empfehlung für Fortsetzung
|
||||
|
||||
1. **Server-Neuaufbau durchführen** (siehe `deployment/docs/guides/server-rebuild-plan.md`)
|
||||
- ✅ Detaillierter Plan erstellt (Debian 13 Trixie + UEFI)
|
||||
- ✅ SSH-Zugriff dokumentiert (`deployment/docs/guides/ssh-access.md`)
|
||||
- ✅ Initial-Server-Setup Playbook erstellt (`deployment/ansible/playbooks/initial-server-setup.yml`)
|
||||
- ✅ Docker-Installation für Debian angepasst
|
||||
- ⏳ Server über Netcup Control Panel zurücksetzen (Debian 13 Trixie UEFI)
|
||||
- ⏳ Komplettes Setup via Ansible
|
||||
2. **Nach Neuaufbau**: PostgreSQL-Stacks, Ansible-Verifikation, End-to-End-Tests
|
||||
|
||||
## 🔧 Bekannte Issues
|
||||
|
||||
- SSH-Key-Problem: `error in libcrypto` - muss behoben werden für automatische Tests
|
||||
- Alternative: Manuelle Tests oder Ansible verwenden
|
||||
|
||||
---
|
||||
|
||||
**Hinweis**: Dieser Status kann mit `cursor-agent resume` fortgesetzt werden.
|
||||
|
||||
374
docs/design-token-system.md
Normal file
374
docs/design-token-system.md
Normal file
@@ -0,0 +1,374 @@
|
||||
# Design Token System Dokumentation
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Design Token System ermöglicht die zentrale Verwaltung von Design-Tokens (Farben, Abstände, Typografie, etc.) in PHP mit automatischer CSS-Generierung. Es unterstützt moderne CSS-Features wie OKLCH-Farben, HDR/P3-Support, Dark Mode und State-basierte Token-Variationen.
|
||||
|
||||
## Architektur
|
||||
|
||||
### Kernkomponenten
|
||||
|
||||
1. **TokenRegistry** - Basis-Klasse für Token-Registries
|
||||
2. **ColorVariation** - Verwaltet Farben mit Light/Dark/P3/Fallback-Varianten
|
||||
3. **ColorManipulator** - Service für Farbmanipulation
|
||||
4. **StateMutation** - Definiert Mutationen für Token-States
|
||||
5. **TokenStateRegistry** - Verwaltet Tokens mit States
|
||||
6. **CssAstBuilder** - Generiert CSS aus Tokens
|
||||
|
||||
### Token-Typen
|
||||
|
||||
- **ColorToken** - Farb-Token mit Variationen
|
||||
- **StatefulColorToken** - Farb-Token mit States (hover, disabled, etc.)
|
||||
- **SpacingToken** - Abstands-Token
|
||||
- **TypographyToken** - Typografie-Token
|
||||
|
||||
## Farbmanipulation
|
||||
|
||||
### ColorManipulator Service
|
||||
|
||||
Der `ColorManipulator` Service bietet Methoden zur Manipulation von Farben:
|
||||
|
||||
```php
|
||||
use App\Framework\Design\Services\ColorManipulator;
|
||||
use App\Framework\Design\ValueObjects\CssColor;
|
||||
use App\Framework\Design\ValueObjects\ColorFormat;
|
||||
|
||||
$manipulator = new ColorManipulator();
|
||||
$primary = new CssColor('oklch(70% 0.2 295)', ColorFormat::OKLCH);
|
||||
|
||||
// Helligkeit anpassen
|
||||
$lighter = $manipulator->lighten($primary, 0.1); // 10% heller
|
||||
$darker = $manipulator->darken($primary, 0.2); // 20% dunkler
|
||||
|
||||
// Sättigung anpassen
|
||||
$moreSaturated = $manipulator->saturate($primary, 0.15);
|
||||
$lessSaturated = $manipulator->desaturate($primary, 0.5);
|
||||
|
||||
// Transparenz ändern
|
||||
$semiTransparent = $manipulator->adjustOpacity($primary, 0.5);
|
||||
|
||||
// Farben mixen
|
||||
$secondary = new CssColor('oklch(60% 0.2 145)', ColorFormat::OKLCH);
|
||||
$mixed = $manipulator->mix($primary, $secondary, 0.3); // 30% primary, 70% secondary
|
||||
|
||||
// Farbton verschieben
|
||||
$shifted = $manipulator->adjustHue($primary, 30); // 30 Grad verschieben
|
||||
```
|
||||
|
||||
### CssColor Methoden
|
||||
|
||||
Farben können auch direkt manipuliert werden:
|
||||
|
||||
```php
|
||||
$primary = new CssColor('oklch(70% 0.2 295)', ColorFormat::OKLCH);
|
||||
|
||||
$hover = $primary->lighten(0.1);
|
||||
$active = $primary->darken(0.15);
|
||||
$disabled = $primary->desaturate(1.0)->withOpacity(0.5);
|
||||
$mixed = $primary->mix($secondary, 0.3);
|
||||
```
|
||||
|
||||
## Token States
|
||||
|
||||
### TokenState Enum
|
||||
|
||||
Das `TokenState` Enum definiert verschiedene States für Tokens:
|
||||
|
||||
- `DEFAULT` - Standard State
|
||||
- `DISABLED` - Deaktiviert
|
||||
- `HOVER` - Hover State
|
||||
- `ACTIVE` - Aktiv (z.B. gedrückt)
|
||||
- `SELECTED` - Ausgewählt
|
||||
- `FOCUSED` - Fokussiert
|
||||
- `LOADING` - Lade-Zustand
|
||||
- `ERROR` - Fehler-Zustand
|
||||
- `SUCCESS` - Erfolgs-Zustand
|
||||
- `WARNING` - Warnung-Zustand
|
||||
|
||||
### StateMutation
|
||||
|
||||
`StateMutation` definiert, wie ein Token für einen bestimmten State mutiert werden soll:
|
||||
|
||||
```php
|
||||
use App\Framework\Design\ValueObjects\StateMutation;
|
||||
use App\Framework\Design\ValueObjects\TokenState;
|
||||
|
||||
// Disabled State: 0% Sättigung, 50% Transparenz
|
||||
$disabledMutation = new StateMutation(
|
||||
saturationAdjustment: -1.0, // 0% Sättigung
|
||||
opacityAdjustment: 0.5 // 50% Transparenz
|
||||
);
|
||||
|
||||
// Hover State: 10% heller
|
||||
$hoverMutation = new StateMutation(
|
||||
lightnessAdjustment: 0.1
|
||||
);
|
||||
|
||||
// Komposition mehrerer Mutationen
|
||||
$desaturateMutation = new StateMutation(saturationAdjustment: -1.0);
|
||||
$opacityMutation = new StateMutation(opacityAdjustment: 0.5);
|
||||
$composedMutation = $desaturateMutation->compose($opacityMutation);
|
||||
```
|
||||
|
||||
### StatefulColorToken
|
||||
|
||||
Ein `StatefulColorToken` repräsentiert einen Farb-Token mit einem spezifischen State:
|
||||
|
||||
```php
|
||||
use App\Framework\Design\ValueObjects\Tokens\StatefulColorToken;
|
||||
use App\Framework\Design\ValueObjects\ColorVariation;
|
||||
use App\Framework\Design\ValueObjects\TokenState;
|
||||
use App\Framework\Design\ValueObjects\StateMutation;
|
||||
|
||||
$primaryVariation = new ColorVariation(
|
||||
name: 'primary',
|
||||
light: new CssColor('oklch(70% 0.2 295)', ColorFormat::OKLCH),
|
||||
dark: new CssColor('oklch(60% 0.22 295)', ColorFormat::OKLCH)
|
||||
);
|
||||
|
||||
// Default State
|
||||
$defaultToken = new StatefulColorToken(
|
||||
name: 'button-primary',
|
||||
variation: $primaryVariation,
|
||||
state: TokenState::DEFAULT
|
||||
);
|
||||
|
||||
// Disabled State (abgeleitet)
|
||||
$disabledToken = new StatefulColorToken(
|
||||
name: 'button-primary',
|
||||
variation: $primaryVariation,
|
||||
state: TokenState::DISABLED,
|
||||
baseState: TokenState::DEFAULT,
|
||||
mutation: new StateMutation(
|
||||
saturationAdjustment: -1.0,
|
||||
opacityAdjustment: 0.5
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### TokenStateRegistry
|
||||
|
||||
Der `TokenStateRegistry` verwaltet Tokens mit States:
|
||||
|
||||
```php
|
||||
use App\Framework\Design\ValueObjects\TokenStateRegistry;
|
||||
use App\Framework\Design\ValueObjects\TokenState;
|
||||
use App\Framework\Design\ValueObjects\StateMutation;
|
||||
|
||||
// Default Mutationen für States definieren
|
||||
$registry = new TokenStateRegistry(
|
||||
tokens: [],
|
||||
stateMutations: [
|
||||
TokenState::DISABLED => new StateMutation(
|
||||
saturationAdjustment: -1.0,
|
||||
opacityAdjustment: 0.5
|
||||
),
|
||||
TokenState::HOVER => new StateMutation(
|
||||
lightnessAdjustment: 0.1
|
||||
),
|
||||
TokenState::ACTIVE => new StateMutation(
|
||||
lightnessAdjustment: -0.15
|
||||
),
|
||||
]
|
||||
);
|
||||
|
||||
// Token hinzufügen
|
||||
$registry = $registry->addToken($defaultToken);
|
||||
|
||||
// Token für State abrufen
|
||||
$hoverToken = $registry->getTokenForState('button-primary', TokenState::HOVER);
|
||||
|
||||
// Alle States für Token abrufen
|
||||
$allStates = $registry->getAllStatesForToken('button-primary');
|
||||
```
|
||||
|
||||
## CSS-Generierung
|
||||
|
||||
### Automatische CSS-Generierung
|
||||
|
||||
Das System generiert automatisch CSS aus Tokens:
|
||||
|
||||
```php
|
||||
use App\Framework\Design\ValueObjects\FrameworkTokenRegistry;
|
||||
|
||||
$registry = new FrameworkTokenRegistry();
|
||||
$css = $registry->toCss('framework-settings', includeHdr: true);
|
||||
```
|
||||
|
||||
### CSS Output
|
||||
|
||||
Das generierte CSS nutzt moderne Features:
|
||||
|
||||
```css
|
||||
@layer framework-settings {
|
||||
:root {
|
||||
--color-primary: oklch(70% 0.2 295);
|
||||
--color-primary-p3: color(display-p3 0.8 0.3 1);
|
||||
}
|
||||
}
|
||||
|
||||
@layer framework-settings {
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-primary: oklch(60% 0.22 295);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer framework-settings {
|
||||
@media (dynamic-range: high) {
|
||||
:root {
|
||||
--color-primary: var(--color-primary-p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### State-basierte CSS mit Pseudo-Klassen
|
||||
|
||||
Für StatefulTokens werden Pseudo-Klassen generiert:
|
||||
|
||||
```css
|
||||
.button-primary {
|
||||
background: oklch(70% 0.2 295);
|
||||
}
|
||||
|
||||
.button-primary:hover {
|
||||
background: oklch(77% 0.2 295); /* Automatisch generiert */
|
||||
}
|
||||
|
||||
.button-primary:active {
|
||||
background: oklch(59.5% 0.2 295); /* Automatisch generiert */
|
||||
}
|
||||
|
||||
.button-primary:disabled {
|
||||
background: oklch(70% 0 295 / 0.5); /* Komponierte Mutation */
|
||||
}
|
||||
```
|
||||
|
||||
## Verwendung
|
||||
|
||||
### FrameworkTokenRegistry
|
||||
|
||||
Framework-weite Tokens:
|
||||
|
||||
```php
|
||||
use App\Framework\Design\ValueObjects\FrameworkTokenRegistry;
|
||||
|
||||
$registry = new FrameworkTokenRegistry();
|
||||
$primaryColor = $registry->get('color-primary');
|
||||
```
|
||||
|
||||
### AdminTokenRegistry
|
||||
|
||||
Admin-spezifische Tokens:
|
||||
|
||||
```php
|
||||
use App\Application\Admin\ValueObjects\AdminTokenRegistry;
|
||||
|
||||
$registry = new AdminTokenRegistry();
|
||||
$adminBg = $registry->get('admin-bg-primary');
|
||||
```
|
||||
|
||||
### Token generieren
|
||||
|
||||
```bash
|
||||
# Framework Tokens generieren
|
||||
php console.php design:generate-tokens --scope=framework
|
||||
|
||||
# Admin Tokens generieren
|
||||
php console.php design:generate-tokens --scope=admin
|
||||
|
||||
# Beide generieren
|
||||
npm run build # Generiert automatisch beide vor dem Build
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Farben definieren
|
||||
|
||||
1. **OKLCH verwenden**: Perzeptuell uniforme Farbmanipulation
|
||||
2. **ColorVariation nutzen**: Zentrale Verwaltung von Light/Dark/P3
|
||||
3. **HDR-Support**: Automatische P3-Varianten für HDR-Displays
|
||||
|
||||
### States definieren
|
||||
|
||||
1. **Basis-State**: Immer einen DEFAULT State definieren
|
||||
2. **Mutationen komponieren**: Mehrere Mutationen kombinieren
|
||||
3. **Konsistente Mutationen**: Gleiche Mutationen für ähnliche States
|
||||
|
||||
### Token-Organisation
|
||||
|
||||
1. **Scopes trennen**: Framework vs. Admin Tokens
|
||||
2. **Naming Convention**: Klare, konsistente Namen
|
||||
3. **Beschreibungen**: Immer Beschreibungen hinzufügen
|
||||
|
||||
## Erweiterte Features
|
||||
|
||||
### Custom State Mutationen
|
||||
|
||||
```php
|
||||
// Komplexe Mutation: Desaturate + Opacity + Mix
|
||||
$complexMutation = new StateMutation(
|
||||
saturationAdjustment: -0.5,
|
||||
opacityAdjustment: 0.7,
|
||||
mixColor: new CssColor('oklch(50% 0 0)', ColorFormat::OKLCH), // Grau
|
||||
mixWeight: 0.3
|
||||
);
|
||||
```
|
||||
|
||||
### State-Hierarchien
|
||||
|
||||
```php
|
||||
// Disabled kann von Hover abgeleitet werden
|
||||
$disabledFromHover = new StatefulColorToken(
|
||||
name: 'button-primary',
|
||||
variation: $primaryVariation,
|
||||
state: TokenState::DISABLED,
|
||||
baseState: TokenState::HOVER, // Basis ist Hover, nicht Default
|
||||
mutation: new StateMutation(
|
||||
saturationAdjustment: -1.0,
|
||||
opacityAdjustment: 0.5
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
- **Caching**: Generierte CSS wird gecacht
|
||||
- **Lazy Loading**: Tokens werden nur bei Bedarf geladen
|
||||
- **Optimierte Konvertierung**: OKLCH-Konvertierung ist optimiert
|
||||
|
||||
## Browser-Unterstützung
|
||||
|
||||
- **OKLCH**: Moderne Browser (Chrome 111+, Safari 15.4+, Firefox 113+)
|
||||
- **Display P3**: HDR-Displays mit Wide-Gamut-Support
|
||||
- **Fallbacks**: Automatische RGB/HEX-Fallbacks für ältere Browser
|
||||
|
||||
## Migration von bestehenden Tokens
|
||||
|
||||
Bestehende Tokens können schrittweise migriert werden:
|
||||
|
||||
1. **ColorVariation erstellen**: Light/Dark Varianten definieren
|
||||
2. **States hinzufügen**: Mutationen für States definieren
|
||||
3. **CSS aktualisieren**: Generierte CSS verwenden
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Farben sehen falsch aus
|
||||
|
||||
- Prüfe OKLCH-Werte (Lightness 0-1 oder 0-100%, Chroma 0-0.4+, Hue 0-360)
|
||||
- Verwende ColorManipulator für konsistente Manipulation
|
||||
|
||||
### States funktionieren nicht
|
||||
|
||||
- Prüfe ob Token StatefulTokenInterface implementiert
|
||||
- Prüfe ob Mutation korrekt definiert ist
|
||||
- Prüfe CSS-Output auf Pseudo-Klassen
|
||||
|
||||
### CSS wird nicht generiert
|
||||
|
||||
- Prüfe ob TokenRegistry korrekt initialisiert ist
|
||||
- Prüfe ob Output-Pfad existiert und beschreibbar ist
|
||||
- Prüfe Console-Output für Fehlermeldungen
|
||||
|
||||
754
docs/features/error-handling/advanced.md
Normal file
754
docs/features/error-handling/advanced.md
Normal file
@@ -0,0 +1,754 @@
|
||||
# Exception System Advanced Features
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das Exception-System wurde um 10 erweiterte Features erweitert, die in drei Phasen implementiert wurden:
|
||||
|
||||
- **Phase 1 (Foundation)**: Rate Limiting, Context Caching, Performance Tracking
|
||||
- **Phase 2 (User Experience)**: User-Friendly Messages, Localization
|
||||
- **Phase 3 (Advanced)**: Recovery/Retry, Pattern Detection, Correlation, Metrics, Health Checks
|
||||
|
||||
Alle Features sind optional und können einzeln aktiviert werden. Sie folgen den Framework-Prinzipien (final, readonly, Value Objects, Composition over Inheritance).
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Foundation Features
|
||||
|
||||
### 1. Exception Rate Limiting & Throttling
|
||||
|
||||
**Ziel**: Verhindert Log-Spam durch wiederholte gleiche Exceptions
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionFingerprint` - Generiert eindeutige Fingerprints für Exception-Gruppierung
|
||||
- `ExceptionRateLimitConfig` - Konfiguration für Thresholds und Time Windows
|
||||
- `ExceptionRateLimiter` - Rate Limiter mit Cache-basiertem Tracking
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\RateLimit\ExceptionRateLimiter;
|
||||
use App\Framework\ExceptionHandling\RateLimit\ExceptionRateLimitConfig;
|
||||
use App\Framework\Core\ValueObjects\Duration;
|
||||
|
||||
// Konfiguration erstellen
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 10,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
|
||||
// Rate Limiter erstellen
|
||||
$rateLimiter = new ExceptionRateLimiter($cache, $config);
|
||||
|
||||
// In ErrorKernel integriert (automatisch)
|
||||
// Exceptions werden automatisch rate-limited, wenn Threshold erreicht wird
|
||||
```
|
||||
|
||||
**Konfiguration**:
|
||||
- `maxExceptions`: Maximale Anzahl gleicher Exceptions pro Time Window (Default: 10)
|
||||
- `timeWindow`: Time Window für Rate Limiting (Default: 60 Sekunden)
|
||||
- `skipLoggingOnLimit`: Skip Logging wenn Rate Limit erreicht (Default: true)
|
||||
- `skipAuditOnLimit`: Skip Audit Logging wenn Rate Limit erreicht (Default: true)
|
||||
- `trackMetricsOnLimit`: Track Metriken auch bei Rate Limit (Default: true)
|
||||
|
||||
**Fingerprint-Generierung**:
|
||||
- Basierend auf Exception-Klasse, normalisierter Message, File/Line
|
||||
- Optional: Component und Operation aus Context für präzisere Gruppierung
|
||||
- Normalisierung entfernt variable Teile (UUIDs, Timestamps, Zahlen, etc.)
|
||||
|
||||
---
|
||||
|
||||
### 2. Exception Context Caching
|
||||
|
||||
**Ziel**: Performance-Optimierung durch Caching von häufig verwendeten Context-Daten
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionContextCache` - Cache-Wrapper für Context-Daten
|
||||
- Integration in `ExceptionContextBuilder` - Automatischer Cache-Lookup
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextCache;
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextBuilder;
|
||||
|
||||
// Context Cache erstellen
|
||||
$contextCache = new ExceptionContextCache($cache);
|
||||
|
||||
// Context Builder mit Cache
|
||||
$builder = new ExceptionContextBuilder(
|
||||
errorScope: $errorScope,
|
||||
contextCache: $contextCache
|
||||
);
|
||||
|
||||
// Automatischer Cache-Lookup beim buildFromRequest()
|
||||
$context = $builder->buildFromRequest($request);
|
||||
```
|
||||
|
||||
**Cache-Levels**:
|
||||
- **Request-Level**: TTL 10 Minuten (spezifischste)
|
||||
- **Session-Level**: TTL 10 Minuten
|
||||
- **User-Level**: TTL 30 Minuten (am wenigsten spezifisch)
|
||||
|
||||
**Cache-Invalidation**:
|
||||
```php
|
||||
// Manuelle Invalidation bei Context-Änderungen
|
||||
$contextCache->invalidateRequest($requestId);
|
||||
$contextCache->invalidateSession($sessionId);
|
||||
$contextCache->invalidateUser($userId);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Exception Performance Tracking
|
||||
|
||||
**Ziel**: Tracking von Performance-Impact pro Exception-Typ
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionPerformanceMetrics` - Value Object für Metriken
|
||||
- `ExceptionPerformanceTracker` - Performance-Tracking
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Performance\ExceptionPerformanceTracker;
|
||||
|
||||
$tracker = new ExceptionPerformanceTracker();
|
||||
|
||||
// Start tracking
|
||||
$startData = $tracker->start();
|
||||
|
||||
// ... exception occurs ...
|
||||
|
||||
// End tracking
|
||||
$metrics = $tracker->end($startData, $exception, $context);
|
||||
|
||||
// Metriken enthalten:
|
||||
// - executionTimeMs: Ausführungszeit in Millisekunden
|
||||
// - memoryDeltaBytes: Memory-Delta in Bytes
|
||||
// - cpuUsagePercent: CPU-Usage in Prozent (wenn verfügbar)
|
||||
```
|
||||
|
||||
**Integration**:
|
||||
- Metriken werden automatisch in `ExceptionContextData::metadata['performance']` gespeichert
|
||||
- Kann mit `MetricsCollector` integriert werden für Monitoring
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: User Experience Features
|
||||
|
||||
### 4. User-Friendly Exception Messages
|
||||
|
||||
**Ziel**: Übersetzung von technischen Exception-Messages zu benutzerfreundlichen Texten
|
||||
|
||||
**Komponenten**:
|
||||
- `UserFriendlyMessage` - Value Object für User-Messages
|
||||
- `ExceptionMessageTranslator` - Message-Übersetzung mit Template-System
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Translation\ExceptionMessageTranslator;
|
||||
use App\Framework\ExceptionHandling\Translation\UserFriendlyMessage;
|
||||
|
||||
// Templates definieren
|
||||
$templates = [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Database connection failed. Please try again later.',
|
||||
'title' => 'Database Error',
|
||||
'help' => 'If this problem persists, please contact support.'
|
||||
],
|
||||
\App\Framework\Exception\ValidationException::class => [
|
||||
'message' => 'Please check your input and try again.',
|
||||
'title' => 'Validation Error'
|
||||
]
|
||||
];
|
||||
|
||||
// Translator erstellen
|
||||
$translator = new ExceptionMessageTranslator(
|
||||
templates: $templates,
|
||||
isDebugMode: false
|
||||
);
|
||||
|
||||
// Message übersetzen
|
||||
$userMessage = $translator->translate($exception, $context);
|
||||
|
||||
// $userMessage->message: User-friendly message
|
||||
// $userMessage->title: Optional title
|
||||
// $userMessage->helpText: Optional help text
|
||||
// $userMessage->technicalMessage: Original technical message (für Debugging)
|
||||
```
|
||||
|
||||
**Template-Variablen**:
|
||||
- `{exception_message}` - Original Exception-Message
|
||||
- `{exception_class}` - Exception-Klasse
|
||||
- `{operation}` - Operation aus Context
|
||||
- `{component}` - Component aus Context
|
||||
|
||||
**Integration**:
|
||||
- Automatisch in `ResponseErrorRenderer` integriert
|
||||
- In Debug-Mode werden technische Messages angezeigt
|
||||
- In Production werden User-Friendly Messages verwendet
|
||||
|
||||
---
|
||||
|
||||
### 5. Exception Localization
|
||||
|
||||
**Ziel**: i18n-Support für Exception-Messages basierend auf User-Locale
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionLocalizer` - Lokalisierung mit Fallback-Chain
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Localization\ExceptionLocalizer;
|
||||
|
||||
// Übersetzungen definieren
|
||||
$translations = [
|
||||
'de' => [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Datenbankverbindung fehlgeschlagen. Bitte versuchen Sie es später erneut.',
|
||||
'title' => 'Datenbankfehler'
|
||||
]
|
||||
],
|
||||
'en' => [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Database connection failed. Please try again later.',
|
||||
'title' => 'Database Error'
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Localizer erstellen
|
||||
$localizer = new ExceptionLocalizer(
|
||||
translations: $translations,
|
||||
defaultLocale: 'en'
|
||||
);
|
||||
|
||||
// Locale aus Context extrahieren
|
||||
$locale = $localizer->getLocale($context);
|
||||
|
||||
// Übersetzungen für Locale abrufen
|
||||
$localeTranslations = $localizer->getTranslations($locale);
|
||||
|
||||
// Fallback-Chain: [user_locale, default_locale, 'en']
|
||||
$fallbackChain = $localizer->getFallbackChain($locale);
|
||||
```
|
||||
|
||||
**Locale-Extraktion**:
|
||||
- Aus `ExceptionContextData::metadata['locale']`
|
||||
- Fallback zu Default-Locale
|
||||
- Fallback zu 'en' als letzte Option
|
||||
|
||||
**Integration**:
|
||||
- Wird von `ExceptionMessageTranslator` verwendet
|
||||
- Locale sollte in `ExceptionContextData::metadata['locale']` gespeichert werden
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Advanced Features
|
||||
|
||||
### 6. Exception Recovery & Retry Logic
|
||||
|
||||
**Ziel**: Automatische Retry-Logik für temporäre Exceptions
|
||||
|
||||
**Komponenten**:
|
||||
- `RetryStrategy` - Retry-Strategien (Exponential Backoff, Linear, Fixed)
|
||||
- `ExceptionRecoveryManager` - Recovery-Manager
|
||||
- `RetryableException` - Marker-Interface
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Recovery\ExceptionRecoveryManager;
|
||||
use App\Framework\ExceptionHandling\Recovery\RetryableException;
|
||||
use App\Framework\Exception\ExceptionMetadata;
|
||||
|
||||
// Retryable Exception implementieren
|
||||
final class NetworkException extends \Exception implements RetryableException
|
||||
{
|
||||
}
|
||||
|
||||
// Exception mit Retry-Config erstellen
|
||||
$metadata = ExceptionMetadata::withRetry(
|
||||
retryAfter: 1000 // Base delay in milliseconds
|
||||
)->withMaxRetries(3)
|
||||
->withRetryStrategy('exponential_backoff');
|
||||
|
||||
// Recovery Manager
|
||||
$recoveryManager = new ExceptionRecoveryManager();
|
||||
|
||||
// Prüfen ob Retry nötig
|
||||
if ($recoveryManager->shouldRetry($exception, $metadata)) {
|
||||
$delay = $recoveryManager->getRetryDelay($exception, $metadata, $attemptNumber);
|
||||
// Retry nach $delay Millisekunden
|
||||
}
|
||||
```
|
||||
|
||||
**Retry-Strategien**:
|
||||
- **Exponential Backoff** (Default): `baseDelay * 2^(attempt-1)`
|
||||
- **Linear**: `baseDelay * attempt`
|
||||
- **Fixed**: `baseDelay` (konstant)
|
||||
|
||||
**ExceptionMetadata Erweiterungen**:
|
||||
- `maxRetries`: Maximale Anzahl Retries (Default: 3)
|
||||
- `retryStrategy`: Retry-Strategie (Default: 'exponential_backoff')
|
||||
- `retryAfter`: Base Delay in Millisekunden
|
||||
|
||||
**Retryable Exceptions**:
|
||||
- Exceptions die `RetryableException` implementieren
|
||||
- Exceptions mit bestimmten Namen-Patterns (NetworkException, TimeoutException, etc.)
|
||||
|
||||
---
|
||||
|
||||
### 7. Exception Pattern Detection & Auto-Fix Suggestions
|
||||
|
||||
**Ziel**: ML-basierte Pattern-Erkennung mit Fix-Vorschlägen
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionPattern` - Value Object für Patterns
|
||||
- `FixSuggestion` - Value Object für Fix-Vorschläge
|
||||
- `ExceptionPatternDetector` - Pattern-Detection
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\PatternDetection\ExceptionPatternDetector;
|
||||
use App\Framework\ExceptionHandling\PatternDetection\FixSuggestion;
|
||||
|
||||
// Knowledge Base definieren
|
||||
$knowledgeBase = [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'description' => 'Database connection timeout',
|
||||
'fixes' => [
|
||||
[
|
||||
'title' => 'Check connection pool size',
|
||||
'description' => 'Increase database connection pool size',
|
||||
'code' => '$config->setMaxConnections(50);',
|
||||
'confidence' => 'high'
|
||||
],
|
||||
[
|
||||
'title' => 'Check database server',
|
||||
'description' => 'Verify database server is running and accessible',
|
||||
'confidence' => 'medium'
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
// Pattern Detector erstellen
|
||||
$detector = new ExceptionPatternDetector($knowledgeBase);
|
||||
|
||||
// Patterns erkennen
|
||||
$patterns = $detector->detect($exception, $context);
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
echo $pattern->description . "\n";
|
||||
foreach ($pattern->fixSuggestions as $fix) {
|
||||
echo " - " . $fix->title . ": " . $fix->description . "\n";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Integration**:
|
||||
- Kann in `ErrorAggregator` integriert werden
|
||||
- Patterns werden in `ExceptionContextData::metadata['patterns']` gespeichert
|
||||
|
||||
---
|
||||
|
||||
### 8. Exception Correlation & Root Cause Analysis
|
||||
|
||||
**Ziel**: Verknüpfung verwandter Exceptions für Root-Cause-Analyse
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionCorrelation` - Value Object für Korrelationen
|
||||
- `ExceptionCorrelationEngine` - Correlation-Engine
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Correlation\ExceptionCorrelationEngine;
|
||||
|
||||
$correlationEngine = new ExceptionCorrelationEngine($cache);
|
||||
|
||||
// Exception korrelieren
|
||||
$correlation = $correlationEngine->correlate($exception, $context);
|
||||
|
||||
// Correlation-Daten:
|
||||
// - correlationKey: Request-ID, Session-ID oder User-ID
|
||||
// - exceptionIds: Array von verwandten Exception-IDs
|
||||
// - rootCauseId: ID der ersten Exception (Root Cause)
|
||||
```
|
||||
|
||||
**Correlation-Keys**:
|
||||
- **Request-ID** (höchste Priorität): Alle Exceptions in derselben Request
|
||||
- **Session-ID**: Alle Exceptions in derselben Session
|
||||
- **User-ID**: Alle Exceptions für denselben User
|
||||
|
||||
**Root Cause Analysis**:
|
||||
- Erste Exception in Correlation-Chain ist Root Cause
|
||||
- Alle weiteren Exceptions sind Folge-Exceptions
|
||||
|
||||
**Integration**:
|
||||
- Correlation-Daten werden in `ExceptionContextData::metadata['correlations']` gespeichert
|
||||
- Kann für Exception-Graph-Visualisierung verwendet werden
|
||||
|
||||
---
|
||||
|
||||
### 9. Exception Metrics & Monitoring Integration
|
||||
|
||||
**Ziel**: Integration mit Monitoring-Systemen (Prometheus, StatsD)
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionMetrics` - Value Object für Metriken
|
||||
- `ExceptionMetricsCollector` - Metrics-Collector
|
||||
- `PrometheusExporter` - Prometheus-Export
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Metrics\ExceptionMetricsCollector;
|
||||
use App\Framework\ExceptionHandling\Metrics\PrometheusExporter;
|
||||
|
||||
$collector = new ExceptionMetricsCollector($cache);
|
||||
|
||||
// Exception-Metrik aufzeichnen
|
||||
$collector->record($exception, $context, $executionTimeMs);
|
||||
|
||||
// Metriken abrufen
|
||||
$metrics = $collector->getMetrics();
|
||||
|
||||
// Prometheus-Format exportieren
|
||||
$exporter = new PrometheusExporter();
|
||||
$prometheusMetrics = $exporter->export($metrics);
|
||||
|
||||
// Output:
|
||||
// exception_total 42
|
||||
// exception_by_class{exception_class="DatabaseException"} 10
|
||||
// exception_by_component{component="UserService"} 5
|
||||
// exception_average_execution_time_ms 125.50
|
||||
```
|
||||
|
||||
**Metriken**:
|
||||
- `totalCount`: Gesamtanzahl Exceptions
|
||||
- `byClass`: Anzahl pro Exception-Klasse
|
||||
- `byComponent`: Anzahl pro Component
|
||||
- `averageExecutionTimeMs`: Durchschnittliche Ausführungszeit
|
||||
|
||||
**Integration**:
|
||||
- Kann in `ProductionMetricsController` integriert werden
|
||||
- Metriken werden im Prometheus-Format exportiert
|
||||
- Real-time Aggregation über Time-Windows
|
||||
|
||||
---
|
||||
|
||||
### 10. Exception Health Checks & Circuit Breakers
|
||||
|
||||
**Ziel**: Circuit Breaker Pattern für Exception-basierte Health Checks
|
||||
|
||||
**Komponenten**:
|
||||
- `ExceptionHealthChecker` - Health Checker
|
||||
|
||||
**Verwendung**:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Health\ExceptionHealthChecker;
|
||||
use App\Framework\ExceptionHandling\Metrics\ExceptionMetricsCollector;
|
||||
|
||||
$metricsCollector = new ExceptionMetricsCollector($cache);
|
||||
$healthChecker = new ExceptionHealthChecker(
|
||||
metricsCollector: $metricsCollector,
|
||||
errorRateThreshold: 0.1, // 10% Error Rate
|
||||
timeWindowSeconds: 60
|
||||
);
|
||||
|
||||
// Health Check durchführen
|
||||
$result = $healthChecker->check();
|
||||
|
||||
// Status:
|
||||
// - Healthy: Error Rate < 5%
|
||||
// - Warning: Error Rate 5-10%
|
||||
// - Unhealthy: Error Rate > 10%
|
||||
```
|
||||
|
||||
**Health Check Status**:
|
||||
- **Healthy**: Error Rate unterhalb des Thresholds
|
||||
- **Warning**: Error Rate bei 50% des Thresholds
|
||||
- **Unhealthy**: Error Rate oberhalb des Thresholds
|
||||
|
||||
**Integration**:
|
||||
- Implementiert `HealthCheckInterface`
|
||||
- Kann in `HealthCheckManager` registriert werden
|
||||
- Nutzt bestehende `CircuitBreaker` Infrastruktur
|
||||
|
||||
---
|
||||
|
||||
## DI Container Integration
|
||||
|
||||
### Beispiel-Konfiguration
|
||||
|
||||
```php
|
||||
// Exception Rate Limiter
|
||||
$container->bind(ExceptionRateLimitConfig::class, function() {
|
||||
return ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 10,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
});
|
||||
|
||||
$container->singleton(ExceptionRateLimiter::class, function($container) {
|
||||
return new ExceptionRateLimiter(
|
||||
cache: $container->get(Cache::class),
|
||||
config: $container->get(ExceptionRateLimitConfig::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Context Cache
|
||||
$container->singleton(ExceptionContextCache::class, function($container) {
|
||||
return new ExceptionContextCache(
|
||||
cache: $container->get(Cache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Context Builder
|
||||
$container->singleton(ExceptionContextBuilder::class, function($container) {
|
||||
return new ExceptionContextBuilder(
|
||||
errorScope: $container->get(ErrorScope::class),
|
||||
contextCache: $container->get(ExceptionContextCache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Performance Tracker
|
||||
$container->singleton(ExceptionPerformanceTracker::class, function() {
|
||||
return new ExceptionPerformanceTracker();
|
||||
});
|
||||
|
||||
// Exception Message Translator
|
||||
$container->singleton(ExceptionMessageTranslator::class, function($container) {
|
||||
$templates = require __DIR__ . '/config/exception_messages.php';
|
||||
return new ExceptionMessageTranslator(
|
||||
templates: $templates,
|
||||
isDebugMode: $container->get('config')->get('app.debug', false)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Localizer
|
||||
$container->singleton(ExceptionLocalizer::class, function() {
|
||||
$translations = require __DIR__ . '/config/exception_translations.php';
|
||||
return new ExceptionLocalizer(
|
||||
translations: $translations,
|
||||
defaultLocale: 'en'
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Recovery Manager
|
||||
$container->singleton(ExceptionRecoveryManager::class, function() {
|
||||
return new ExceptionRecoveryManager();
|
||||
});
|
||||
|
||||
// Exception Pattern Detector
|
||||
$container->singleton(ExceptionPatternDetector::class, function() {
|
||||
$knowledgeBase = require __DIR__ . '/config/exception_patterns.php';
|
||||
return new ExceptionPatternDetector($knowledgeBase);
|
||||
});
|
||||
|
||||
// Exception Correlation Engine
|
||||
$container->singleton(ExceptionCorrelationEngine::class, function($container) {
|
||||
return new ExceptionCorrelationEngine(
|
||||
cache: $container->get(Cache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Metrics Collector
|
||||
$container->singleton(ExceptionMetricsCollector::class, function($container) {
|
||||
return new ExceptionMetricsCollector(
|
||||
cache: $container->get(Cache::class)
|
||||
);
|
||||
});
|
||||
|
||||
// Exception Health Checker
|
||||
$container->singleton(ExceptionHealthChecker::class, function($container) {
|
||||
return new ExceptionHealthChecker(
|
||||
metricsCollector: $container->get(ExceptionMetricsCollector::class),
|
||||
errorRateThreshold: 0.1,
|
||||
timeWindowSeconds: 60
|
||||
);
|
||||
});
|
||||
|
||||
// ErrorKernel mit allen Features
|
||||
$container->singleton(ErrorKernel::class, function($container) {
|
||||
return new ErrorKernel(
|
||||
rendererFactory: $container->get(ErrorRendererFactory::class),
|
||||
reporter: $container->get(Reporter::class),
|
||||
errorAggregator: $container->get(ErrorAggregatorInterface::class),
|
||||
contextProvider: $container->get(ExceptionContextProvider::class),
|
||||
auditLogger: $container->get(ExceptionAuditLogger::class),
|
||||
rateLimiter: $container->get(ExceptionRateLimiter::class), // NEU
|
||||
executionContext: $container->get(ExecutionContext::class),
|
||||
consoleOutput: $container->get(ConsoleOutput::class),
|
||||
isDebugMode: $container->get('config')->get('app.debug', false)
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Rate Limiting konfigurieren
|
||||
|
||||
```php
|
||||
// Für Production: Strikte Limits
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 5,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
|
||||
// Für Development: Lockerere Limits
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 50,
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
```
|
||||
|
||||
### 2. Context Caching nutzen
|
||||
|
||||
```php
|
||||
// Context Cache nur aktivieren wenn Context-Erstellung teuer ist
|
||||
// (z.B. DB-Queries, externe API-Calls)
|
||||
if ($needsCaching) {
|
||||
$builder = new ExceptionContextBuilder(
|
||||
errorScope: $errorScope,
|
||||
contextCache: $contextCache
|
||||
);
|
||||
} else {
|
||||
$builder = new ExceptionContextBuilder(errorScope: $errorScope);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. User-Friendly Messages definieren
|
||||
|
||||
```php
|
||||
// Alle wichtigen Exceptions sollten User-Friendly Messages haben
|
||||
$templates = [
|
||||
\App\Framework\Exception\DatabaseException::class => [
|
||||
'message' => 'Database connection failed. Please try again later.',
|
||||
'title' => 'Database Error',
|
||||
'help' => 'If this problem persists, please contact support.'
|
||||
],
|
||||
\App\Framework\Exception\ValidationException::class => [
|
||||
'message' => 'Please check your input and try again.',
|
||||
'title' => 'Validation Error'
|
||||
]
|
||||
];
|
||||
```
|
||||
|
||||
### 4. Retry-Strategien wählen
|
||||
|
||||
```php
|
||||
// Für Network-Exceptions: Exponential Backoff
|
||||
$metadata = ExceptionMetadata::withRetry(1000)
|
||||
->withRetryStrategy('exponential_backoff');
|
||||
|
||||
// Für Rate-Limited APIs: Linear
|
||||
$metadata = ExceptionMetadata::withRetry(2000)
|
||||
->withRetryStrategy('linear');
|
||||
|
||||
// Für Polling: Fixed Delay
|
||||
$metadata = ExceptionMetadata::withRetry(5000)
|
||||
->withRetryStrategy('fixed');
|
||||
```
|
||||
|
||||
### 5. Health Checks konfigurieren
|
||||
|
||||
```php
|
||||
// Für kritische Services: Strikte Thresholds
|
||||
$healthChecker = new ExceptionHealthChecker(
|
||||
metricsCollector: $metricsCollector,
|
||||
errorRateThreshold: 0.05, // 5%
|
||||
timeWindowSeconds: 60
|
||||
);
|
||||
|
||||
// Für weniger kritische Services: Lockerere Thresholds
|
||||
$healthChecker = new ExceptionHealthChecker(
|
||||
metricsCollector: $metricsCollector,
|
||||
errorRateThreshold: 0.2, // 20%
|
||||
timeWindowSeconds: 300
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### Von altem System migrieren
|
||||
|
||||
1. **Rate Limiting aktivieren**:
|
||||
```php
|
||||
// In DI Container
|
||||
$container->singleton(ExceptionRateLimiter::class, ...);
|
||||
```
|
||||
|
||||
2. **Context Caching aktivieren** (optional):
|
||||
```php
|
||||
// Nur wenn Performance-Probleme auftreten
|
||||
$container->singleton(ExceptionContextCache::class, ...);
|
||||
```
|
||||
|
||||
3. **User-Friendly Messages hinzufügen**:
|
||||
```php
|
||||
// Templates in config/exception_messages.php definieren
|
||||
```
|
||||
|
||||
4. **Health Checks registrieren**:
|
||||
```php
|
||||
// In HealthCheckManager
|
||||
$healthCheckManager->register($exceptionHealthChecker);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Rate Limiting blockiert zu viele Exceptions
|
||||
|
||||
**Problem**: Rate Limiting blockiert auch wichtige Exceptions
|
||||
|
||||
**Lösung**: Threshold erhöhen oder Time Window anpassen
|
||||
```php
|
||||
$config = ExceptionRateLimitConfig::withLimits(
|
||||
maxExceptions: 20, // Erhöht
|
||||
timeWindow: Duration::fromSeconds(60)
|
||||
);
|
||||
```
|
||||
|
||||
### Context Cache liefert veraltete Daten
|
||||
|
||||
**Problem**: Context Cache enthält veraltete User/Session-Daten
|
||||
|
||||
**Lösung**: Cache bei User/Session-Änderungen invalidieren
|
||||
```php
|
||||
$contextCache->invalidateUser($userId);
|
||||
$contextCache->invalidateSession($sessionId);
|
||||
```
|
||||
|
||||
### Performance Tracking zeigt keine Daten
|
||||
|
||||
**Problem**: Performance-Metriken werden nicht aufgezeichnet
|
||||
|
||||
**Lösung**: Sicherstellen dass `ExceptionPerformanceTracker` in `ErrorKernel` integriert ist
|
||||
```php
|
||||
// Performance-Tracking manuell starten
|
||||
$startData = $performanceTracker->start();
|
||||
// ... exception occurs ...
|
||||
$metrics = $performanceTracker->end($startData, $exception, $context);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weitere Ressourcen
|
||||
|
||||
- [Error Handling Guidelines](./error-handling.md)
|
||||
- [Exception Architecture](../ERROR-HANDLING-UNIFIED-ARCHITECTURE.md)
|
||||
- [Audit Logging](./audit-logging.md)
|
||||
|
||||
645
docs/features/error-handling/guide.md
Normal file
645
docs/features/error-handling/guide.md
Normal file
@@ -0,0 +1,645 @@
|
||||
# Error Handling & Debugging
|
||||
|
||||
This guide covers error handling patterns and debugging strategies in the framework.
|
||||
|
||||
## Exception Handling
|
||||
|
||||
All custom exceptions in the framework must extend `FrameworkException` to ensure consistent error handling, logging, and recovery mechanisms.
|
||||
|
||||
### The FrameworkException System
|
||||
|
||||
The framework provides a sophisticated exception system with:
|
||||
- **ExceptionContext**: Rich context information for debugging
|
||||
- **ErrorCode**: Categorized error codes with recovery hints
|
||||
- **RetryAfter**: Support for recoverable operations
|
||||
- **Fluent Interface**: Easy context building
|
||||
|
||||
### Creating Custom Exceptions
|
||||
|
||||
```php
|
||||
namespace App\Domain\User\Exceptions;
|
||||
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
use App\Framework\Exception\Core\DatabaseErrorCode;
|
||||
use App\Framework\Exception\ExceptionContext;
|
||||
|
||||
final class UserNotFoundException extends FrameworkException
|
||||
{
|
||||
public static function byId(UserId $id): self
|
||||
{
|
||||
return self::create(
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND,
|
||||
"User with ID '{$id->toString()}' not found"
|
||||
)->withData([
|
||||
'user_id' => $id->toString(),
|
||||
'search_type' => 'by_id'
|
||||
]);
|
||||
}
|
||||
|
||||
public static function byEmail(Email $email): self
|
||||
{
|
||||
$context = ExceptionContext::forOperation('user.lookup', 'UserRepository')
|
||||
->withData(['email' => $email->getMasked()]);
|
||||
|
||||
return self::fromContext(
|
||||
"User with email not found",
|
||||
$context,
|
||||
DatabaseErrorCode::ENTITY_NOT_FOUND
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using ErrorCode Enums
|
||||
|
||||
The framework provides category-specific error code enums for better organization and type safety:
|
||||
|
||||
```php
|
||||
use App\Framework\Exception\Core\DatabaseErrorCode;
|
||||
use App\Framework\Exception\Core\AuthErrorCode;
|
||||
use App\Framework\Exception\Core\HttpErrorCode;
|
||||
use App\Framework\Exception\Core\SecurityErrorCode;
|
||||
use App\Framework\Exception\Core\ValidationErrorCode;
|
||||
|
||||
// Database errors
|
||||
DatabaseErrorCode::CONNECTION_FAILED
|
||||
DatabaseErrorCode::QUERY_FAILED
|
||||
DatabaseErrorCode::TRANSACTION_FAILED
|
||||
DatabaseErrorCode::CONSTRAINT_VIOLATION
|
||||
|
||||
// Authentication errors
|
||||
AuthErrorCode::CREDENTIALS_INVALID
|
||||
AuthErrorCode::TOKEN_EXPIRED
|
||||
AuthErrorCode::SESSION_EXPIRED
|
||||
AuthErrorCode::ACCOUNT_LOCKED
|
||||
|
||||
// HTTP errors
|
||||
HttpErrorCode::BAD_REQUEST
|
||||
HttpErrorCode::NOT_FOUND
|
||||
HttpErrorCode::METHOD_NOT_ALLOWED
|
||||
HttpErrorCode::RATE_LIMIT_EXCEEDED
|
||||
|
||||
// Security errors
|
||||
SecurityErrorCode::CSRF_TOKEN_INVALID
|
||||
SecurityErrorCode::SQL_INJECTION_DETECTED
|
||||
SecurityErrorCode::XSS_DETECTED
|
||||
SecurityErrorCode::PATH_TRAVERSAL_DETECTED
|
||||
|
||||
// Validation errors
|
||||
ValidationErrorCode::INVALID_INPUT
|
||||
ValidationErrorCode::REQUIRED_FIELD_MISSING
|
||||
ValidationErrorCode::BUSINESS_RULE_VIOLATION
|
||||
ValidationErrorCode::INVALID_FORMAT
|
||||
|
||||
// Using error codes in exceptions:
|
||||
throw FrameworkException::create(
|
||||
DatabaseErrorCode::QUERY_FAILED,
|
||||
"Failed to execute user query"
|
||||
)->withContext(
|
||||
ExceptionContext::forOperation('user.find', 'UserRepository')
|
||||
->withData(['query' => 'SELECT * FROM users WHERE id = ?'])
|
||||
->withDebug(['bind_params' => [$userId]])
|
||||
);
|
||||
```
|
||||
|
||||
### Exception Context Building
|
||||
|
||||
```php
|
||||
// Method 1: Using factory methods
|
||||
$exception = FrameworkException::forOperation(
|
||||
'payment.process',
|
||||
'PaymentService',
|
||||
'Payment processing failed',
|
||||
HttpErrorCode::BAD_GATEWAY
|
||||
)->withData([
|
||||
'amount' => $amount->toArray(),
|
||||
'gateway' => 'stripe',
|
||||
'customer_id' => $customerId
|
||||
])->withMetadata([
|
||||
'attempt' => 1,
|
||||
'idempotency_key' => $idempotencyKey
|
||||
]);
|
||||
|
||||
// Method 2: Building context separately
|
||||
$context = ExceptionContext::empty()
|
||||
->withOperation('order.validate', 'OrderService')
|
||||
->withData([
|
||||
'order_id' => $orderId,
|
||||
'total' => $total->toDecimal()
|
||||
])
|
||||
->withDebug([
|
||||
'validation_rules' => ['min_amount', 'max_items'],
|
||||
'failed_rule' => 'min_amount'
|
||||
]);
|
||||
|
||||
throw FrameworkException::fromContext(
|
||||
'Order validation failed',
|
||||
$context,
|
||||
ValidationErrorCode::BUSINESS_RULE_VIOLATION
|
||||
);
|
||||
```
|
||||
|
||||
### Recoverable Exceptions
|
||||
|
||||
```php
|
||||
// Creating recoverable exceptions with retry hints
|
||||
final class RateLimitException extends FrameworkException
|
||||
{
|
||||
public static function exceeded(int $retryAfter): self
|
||||
{
|
||||
return self::create(
|
||||
HttpErrorCode::RATE_LIMIT_EXCEEDED,
|
||||
'API rate limit exceeded'
|
||||
)->withRetryAfter($retryAfter)
|
||||
->withData(['retry_after_seconds' => $retryAfter]);
|
||||
}
|
||||
}
|
||||
|
||||
// Using in code
|
||||
try {
|
||||
$response = $apiClient->request($endpoint);
|
||||
} catch (RateLimitException $e) {
|
||||
if ($e->isRecoverable()) {
|
||||
$waitTime = $e->getRetryAfter();
|
||||
// Schedule retry after $waitTime seconds
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Categories
|
||||
|
||||
```php
|
||||
// Check exception category for handling strategies
|
||||
try {
|
||||
$result = $operation->execute();
|
||||
} catch (FrameworkException $e) {
|
||||
if ($e->isCategory('AUTH')) {
|
||||
// Handle authentication errors
|
||||
return $this->redirectToLogin();
|
||||
}
|
||||
|
||||
if ($e->isCategory('VAL')) {
|
||||
// Handle validation errors
|
||||
return $this->validationErrorResponse($e);
|
||||
}
|
||||
|
||||
if ($e->isErrorCode(DatabaseErrorCode::CONNECTION_FAILED)) {
|
||||
// Handle specific database connection errors
|
||||
$this->notifyOps($e);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Simple Exceptions for Quick Use
|
||||
|
||||
```php
|
||||
// When you don't need the full context system
|
||||
throw FrameworkException::simple('Quick error message');
|
||||
|
||||
// With previous exception
|
||||
} catch (\PDOException $e) {
|
||||
throw FrameworkException::simple(
|
||||
'Database operation failed',
|
||||
$e,
|
||||
500
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Exception Data Sanitization
|
||||
|
||||
The framework automatically sanitizes sensitive data in exceptions:
|
||||
|
||||
```php
|
||||
// Sensitive keys are automatically redacted
|
||||
$exception->withData([
|
||||
'username' => 'john@example.com',
|
||||
'password' => 'secret123', // Will be logged as '[REDACTED]'
|
||||
'api_key' => 'sk_live_...' // Will be logged as '[REDACTED]'
|
||||
]);
|
||||
```
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Always extend FrameworkException** for custom exceptions
|
||||
2. **Use ErrorCode enum** for categorizable errors
|
||||
3. **Provide rich context** with operation, component, and data
|
||||
4. **Use factory methods** for consistent exception creation
|
||||
5. **Sanitize sensitive data** (automatic for common keys)
|
||||
6. **Make exceptions domain-specific** (UserNotFoundException vs generic NotFoundException)
|
||||
7. **Include recovery hints** for recoverable errors
|
||||
|
||||
## Unified Error Kernel Architecture
|
||||
|
||||
The framework uses a **context-aware error handling system** centered around the `ErrorKernel` class that automatically detects execution context (CLI vs HTTP) and handles errors accordingly.
|
||||
|
||||
### ErrorKernel Overview
|
||||
|
||||
**Location**: `src/Framework/ExceptionHandling/ErrorKernel.php`
|
||||
|
||||
**Key Features**:
|
||||
- Automatic context detection (CLI vs HTTP)
|
||||
- Colored console output for CLI errors
|
||||
- HTTP Response objects for web errors
|
||||
- Integration with OWASP Security Event System
|
||||
- Unified error logging via LogReporter
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\ErrorKernel;
|
||||
|
||||
final readonly class ErrorKernel
|
||||
{
|
||||
public function __construct(
|
||||
private ErrorRendererFactory $rendererFactory = new ErrorRendererFactory,
|
||||
private ?ExecutionContext $executionContext = null,
|
||||
private ?ConsoleOutput $consoleOutput = null
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Context-aware exception handler
|
||||
* - CLI: Colored console output
|
||||
* - HTTP: Logs error (middleware creates response)
|
||||
*/
|
||||
public function handle(Throwable $e, array $context = []): mixed
|
||||
{
|
||||
// Automatic logging
|
||||
$log = new LogReporter();
|
||||
$log->report($e);
|
||||
|
||||
// Context-aware handling
|
||||
$executionContext = $this->executionContext ?? ExecutionContext::detect();
|
||||
|
||||
if ($executionContext->isCli()) {
|
||||
$this->handleCliException($e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// HTTP context - middleware will create response
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create HTTP Response from exception (for middleware recovery)
|
||||
*/
|
||||
public function createHttpResponse(
|
||||
Throwable $exception,
|
||||
?ExceptionContextProvider $contextProvider = null,
|
||||
bool $isDebugMode = false
|
||||
): Response {
|
||||
$renderer = new ResponseErrorRenderer($isDebugMode);
|
||||
return $renderer->createResponse($exception, $contextProvider);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Error Handling
|
||||
|
||||
**CliErrorHandler** registers global PHP error handlers for CLI context:
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\CliErrorHandler;
|
||||
use App\Framework\Console\ConsoleOutput;
|
||||
|
||||
// Registration in AppBootstrapper
|
||||
$output = new ConsoleOutput();
|
||||
$cliErrorHandler = new CliErrorHandler($output);
|
||||
$cliErrorHandler->register();
|
||||
|
||||
// Automatic colored output for errors:
|
||||
// - Red for uncaught exceptions
|
||||
// - Yellow for warnings
|
||||
// - Cyan for notices
|
||||
// - Full stack traces in CLI
|
||||
```
|
||||
|
||||
### HTTP Error Handling
|
||||
|
||||
**ExceptionHandlingMiddleware** catches exceptions in HTTP request pipeline:
|
||||
|
||||
```php
|
||||
use App\Framework\Http\Middlewares\ExceptionHandlingMiddleware;
|
||||
|
||||
#[MiddlewarePriorityAttribute(MiddlewarePriority::ERROR_HANDLING)]
|
||||
final readonly class ExceptionHandlingMiddleware implements HttpMiddleware
|
||||
{
|
||||
public function __invoke(
|
||||
MiddlewareContext $context,
|
||||
Next $next,
|
||||
RequestStateManager $stateManager
|
||||
): MiddlewareContext {
|
||||
try {
|
||||
return $next($context);
|
||||
} catch (\Throwable $e) {
|
||||
// Log exception
|
||||
$this->logger->error('Unhandled exception in HTTP request', [
|
||||
'exception' => get_class($e),
|
||||
'message' => $e->getMessage(),
|
||||
]);
|
||||
|
||||
// Create HTTP Response
|
||||
$errorKernel = new ErrorKernel();
|
||||
$response = $errorKernel->createHttpResponse(
|
||||
$e,
|
||||
null,
|
||||
isDebugMode: false
|
||||
);
|
||||
|
||||
return $context->withResponse($response);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### OWASP Security Event Integration
|
||||
|
||||
Exceptions can trigger OWASP security events for audit logging:
|
||||
|
||||
```php
|
||||
use App\Application\Security\OWASPSecurityEventLogger;
|
||||
use App\Application\Security\OWASPEventIdentifier;
|
||||
|
||||
// Automatic security logging
|
||||
try {
|
||||
$this->authenticateUser($credentials);
|
||||
} catch (AuthenticationException $e) {
|
||||
// ErrorKernel logs exception
|
||||
$this->errorKernel->handle($e);
|
||||
|
||||
// OWASP event for security audit trail
|
||||
$this->eventDispatcher->dispatch(
|
||||
new AuthenticationFailedEvent(
|
||||
OWASPEventIdentifier::AUTHN_LOGIN_FAILURE,
|
||||
$credentials->username,
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
```
|
||||
|
||||
### Legacy ErrorHandling Module Removed
|
||||
|
||||
**IMPORTANT**: The legacy `ErrorHandling` module (`src/Framework/ErrorHandling/`) has been **completely removed** as of the unified exception architecture migration.
|
||||
|
||||
**Migration Path**:
|
||||
- All error handling now uses `ErrorKernel` and `FrameworkException`
|
||||
- CLI errors: `CliErrorHandler` → `ErrorKernel`
|
||||
- HTTP errors: `ExceptionHandlingMiddleware` → `ErrorKernel`
|
||||
- Security events: Direct event dispatch via `EventDispatcher`
|
||||
|
||||
**Old Pattern** (removed):
|
||||
```php
|
||||
// ❌ Legacy - NO LONGER EXISTS
|
||||
use App\Framework\ErrorHandling\ErrorHandler;
|
||||
use App\Framework\ErrorHandling\SecurityEventLogger;
|
||||
|
||||
$errorHandler = new ErrorHandler();
|
||||
$errorHandler->register();
|
||||
```
|
||||
|
||||
**New Pattern** (current):
|
||||
```php
|
||||
// ✅ Unified - ErrorKernel
|
||||
use App\Framework\ExceptionHandling\ErrorKernel;
|
||||
|
||||
$errorKernel = new ErrorKernel();
|
||||
$errorKernel->handle($exception);
|
||||
```
|
||||
|
||||
## Logging Best Practices
|
||||
|
||||
### Automatic Exception Logging
|
||||
|
||||
All exceptions handled by `ErrorKernel` are automatically logged via `LogReporter`:
|
||||
|
||||
```php
|
||||
// Automatic logging happens in ErrorKernel::handle()
|
||||
$log = new LogReporter();
|
||||
$log->report($exception);
|
||||
|
||||
// Logs include:
|
||||
// - Exception class and message
|
||||
// - Stack trace
|
||||
// - File and line number
|
||||
// - Context data
|
||||
```
|
||||
|
||||
### Manual Logging
|
||||
|
||||
```php
|
||||
use App\Framework\Logging\Logger;
|
||||
|
||||
// Log exceptions with context
|
||||
try {
|
||||
$user = $this->userRepository->find($userId);
|
||||
} catch (UserNotFoundException $e) {
|
||||
Logger::error('User lookup failed', [
|
||||
'user_id' => $userId,
|
||||
'exception' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Log levels
|
||||
Logger::debug('Debugging information');
|
||||
Logger::info('Informational message');
|
||||
Logger::warning('Warning condition');
|
||||
Logger::error('Error condition');
|
||||
Logger::critical('Critical failure');
|
||||
```
|
||||
|
||||
## Debug Strategies
|
||||
|
||||
### Development vs Production
|
||||
|
||||
**Development** (`APP_DEBUG=true`):
|
||||
- Full stack traces displayed
|
||||
- Detailed error messages
|
||||
- Debug data in responses
|
||||
- SQL query logging
|
||||
|
||||
**Production** (`APP_DEBUG=false`):
|
||||
- Generic error messages
|
||||
- Stack traces hidden from users
|
||||
- Errors logged server-side
|
||||
- Security-safe responses
|
||||
|
||||
### Debugging Tools
|
||||
|
||||
```php
|
||||
// Enable debug mode in ErrorKernel
|
||||
$errorKernel = new ErrorKernel(
|
||||
executionContext: ExecutionContext::cli(),
|
||||
consoleOutput: new ConsoleOutput()
|
||||
);
|
||||
|
||||
// HTTP Response with debug mode
|
||||
$response = $errorKernel->createHttpResponse(
|
||||
$exception,
|
||||
$contextProvider,
|
||||
isDebugMode: true // Shows stack trace in response
|
||||
);
|
||||
```
|
||||
|
||||
### Error Context Providers
|
||||
|
||||
```php
|
||||
use App\Framework\ExceptionHandling\Context\ExceptionContextProvider;
|
||||
|
||||
// Attach custom context to exceptions
|
||||
$contextProvider = new ExceptionContextProvider();
|
||||
$contextProvider->attachContext($exception, [
|
||||
'request_id' => $requestId,
|
||||
'user_id' => $userId,
|
||||
'operation' => 'payment.process'
|
||||
]);
|
||||
|
||||
$response = $errorKernel->createHttpResponse(
|
||||
$exception,
|
||||
$contextProvider,
|
||||
isDebugMode: false
|
||||
);
|
||||
```
|
||||
|
||||
## Error Recovery Patterns
|
||||
|
||||
### Graceful Degradation
|
||||
|
||||
```php
|
||||
// Try primary service, fallback to secondary
|
||||
try {
|
||||
return $this->primaryCache->get($key);
|
||||
} catch (CacheException $e) {
|
||||
Logger::warning('Primary cache failed, using fallback', [
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return $this->fallbackCache->get($key);
|
||||
}
|
||||
```
|
||||
|
||||
### Circuit Breaker Pattern
|
||||
|
||||
```php
|
||||
use App\Framework\Resilience\CircuitBreaker;
|
||||
|
||||
$circuitBreaker = new CircuitBreaker(
|
||||
failureThreshold: 5,
|
||||
timeout: Duration::fromSeconds(60)
|
||||
);
|
||||
|
||||
try {
|
||||
return $circuitBreaker->call(function() {
|
||||
return $this->externalApi->request($endpoint);
|
||||
});
|
||||
} catch (CircuitOpenException $e) {
|
||||
// Circuit is open - use cached response
|
||||
return $this->cachedResponse;
|
||||
}
|
||||
```
|
||||
|
||||
### Retry with Exponential Backoff
|
||||
|
||||
```php
|
||||
use App\Framework\Queue\ValueObjects\RetryStrategy;
|
||||
|
||||
$retryStrategy = new ExponentialBackoffStrategy(
|
||||
maxAttempts: 3,
|
||||
baseDelaySeconds: 60
|
||||
);
|
||||
|
||||
$attempt = 0;
|
||||
while ($attempt < $retryStrategy->getMaxAttempts()) {
|
||||
try {
|
||||
return $this->performOperation();
|
||||
} catch (TransientException $e) {
|
||||
$attempt++;
|
||||
if (!$retryStrategy->shouldRetry($attempt)) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$delay = $retryStrategy->getDelay($attempt);
|
||||
sleep($delay->toSeconds());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Error Scenarios
|
||||
|
||||
### 1. Database Connection Failure
|
||||
|
||||
```php
|
||||
try {
|
||||
$connection = $this->connectionPool->getConnection();
|
||||
} catch (ConnectionException $e) {
|
||||
// Log error
|
||||
$this->errorKernel->handle($e);
|
||||
|
||||
// Return cached data or error response
|
||||
return $this->getCachedData() ?? $this->errorResponse();
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Validation Errors
|
||||
|
||||
```php
|
||||
try {
|
||||
$user = User::create($email, $name);
|
||||
} catch (ValidationException $e) {
|
||||
// Return validation errors to user
|
||||
return new JsonResult([
|
||||
'errors' => $e->getErrors()
|
||||
], status: Status::UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Authentication Failures
|
||||
|
||||
```php
|
||||
try {
|
||||
$user = $this->authenticator->authenticate($credentials);
|
||||
} catch (AuthenticationException $e) {
|
||||
// Log security event
|
||||
$this->eventDispatcher->dispatch(
|
||||
new AuthenticationFailedEvent($credentials->username)
|
||||
);
|
||||
|
||||
// Return 401 Unauthorized
|
||||
return new JsonResult([
|
||||
'error' => 'Invalid credentials'
|
||||
], status: Status::UNAUTHORIZED);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Resource Not Found
|
||||
|
||||
```php
|
||||
try {
|
||||
$order = $this->orderRepository->find($orderId);
|
||||
} catch (OrderNotFoundException $e) {
|
||||
// Return 404 Not Found
|
||||
return new JsonResult([
|
||||
'error' => 'Order not found'
|
||||
], status: Status::NOT_FOUND);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Rate Limit Exceeded
|
||||
|
||||
```php
|
||||
try {
|
||||
$this->rateLimiter->checkLimit($userId);
|
||||
} catch (RateLimitException $e) {
|
||||
// Return 429 Too Many Requests with retry hint
|
||||
return new JsonResult([
|
||||
'error' => 'Rate limit exceeded',
|
||||
'retry_after' => $e->getRetryAfter()
|
||||
], status: Status::TOO_MANY_REQUESTS);
|
||||
}
|
||||
```
|
||||
@@ -106,6 +106,7 @@ That's it! The counter is now fully interactive with zero JavaScript written.
|
||||
- [Security Guide](security-guide.md) - CSRF, rate limiting, input validation
|
||||
- [Performance Guide](performance-guide.md) - Optimization strategies and best practices
|
||||
- [Advanced Features](advanced-features.md) - Fragments, batching, SSE, optimistic UI
|
||||
- [UI Integration Guide](ui-integration-guide.md) - Tooltips, loading states, dialogs, notifications
|
||||
|
||||
### Reference
|
||||
- [API Reference](api-reference.md) - Complete API documentation
|
||||
|
||||
717
docs/livecomponents/ui-integration-guide.md
Normal file
717
docs/livecomponents/ui-integration-guide.md
Normal file
@@ -0,0 +1,717 @@
|
||||
# LiveComponents UI Integration Guide
|
||||
|
||||
**Complete Guide to UI Features: Tooltips, Loading States, Dialogs, and Notifications**
|
||||
|
||||
This guide covers the integrated UI features available in LiveComponents, including tooltips, skeleton loading, dialogs, modals, notifications, and loading states.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Tooltip System](#tooltip-system)
|
||||
2. [Loading States & Skeleton Loading](#loading-states--skeleton-loading)
|
||||
3. [UI Helper System](#ui-helper-system)
|
||||
4. [Notification Component](#notification-component)
|
||||
5. [Dialog & Modal Integration](#dialog--modal-integration)
|
||||
6. [Best Practices](#best-practices)
|
||||
|
||||
---
|
||||
|
||||
## Tooltip System
|
||||
|
||||
### Overview
|
||||
|
||||
The Tooltip System provides automatic tooltip initialization and management for LiveComponent elements. Tooltips are automatically initialized when components are mounted and cleaned up when components are destroyed.
|
||||
|
||||
**Features**:
|
||||
- Automatic initialization for `data-tooltip` attributes
|
||||
- Accessibility support (ARIA attributes)
|
||||
- Smart positioning (viewport-aware)
|
||||
- Validation error tooltips
|
||||
- Automatic cleanup
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```html
|
||||
<!-- Simple tooltip -->
|
||||
<button
|
||||
data-lc-action="save"
|
||||
data-tooltip="Save your changes"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
|
||||
<!-- Tooltip with validation error -->
|
||||
<input
|
||||
type="email"
|
||||
data-lc-action="validateEmail"
|
||||
data-tooltip="Enter a valid email address"
|
||||
data-tooltip-error="Invalid email format"
|
||||
/>
|
||||
```
|
||||
|
||||
### Server-Side Validation Tooltips
|
||||
|
||||
```php
|
||||
use App\Framework\LiveComponents\Attributes\Action;
|
||||
use App\Framework\LiveComponents\ValueObjects\LiveComponentError;
|
||||
|
||||
final class UserForm extends LiveComponent
|
||||
{
|
||||
#[Action]
|
||||
public function validateEmail(string $email): void
|
||||
{
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
// Error will automatically show tooltip if element has data-tooltip-error
|
||||
throw LiveComponentError::validation(
|
||||
'Invalid email format',
|
||||
['field' => 'email'],
|
||||
$this->id->toString(),
|
||||
'validateEmail'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Tooltip Configuration
|
||||
|
||||
```javascript
|
||||
// Configure tooltip behavior globally
|
||||
import { tooltipManager } from './modules/livecomponent/TooltipManager.js';
|
||||
|
||||
// Adjust delays
|
||||
tooltipManager.tooltipDelay = 500; // Show after 500ms
|
||||
tooltipManager.hideDelay = 200; // Hide after 200ms
|
||||
```
|
||||
|
||||
### Tooltip Events
|
||||
|
||||
```javascript
|
||||
// Listen for tooltip events
|
||||
window.addEventListener('livecomponent:tooltip-shown', (e) => {
|
||||
const { element, tooltipText } = e.detail;
|
||||
console.log(`Tooltip shown: ${tooltipText}`);
|
||||
});
|
||||
|
||||
window.addEventListener('livecomponent:tooltip-hidden', (e) => {
|
||||
const { element } = e.detail;
|
||||
console.log('Tooltip hidden');
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Loading States & Skeleton Loading
|
||||
|
||||
### Overview
|
||||
|
||||
The Loading State System provides configurable loading indicators during LiveComponent actions, including skeleton loaders, spinners, and progress indicators.
|
||||
|
||||
**Features**:
|
||||
- Fragment-specific loading
|
||||
- Configurable loading types (skeleton, spinner, progress, none)
|
||||
- Smooth transitions
|
||||
- Optimistic UI integration (no loading for optimistic actions)
|
||||
- Per-component configuration
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```html
|
||||
<!-- Component with skeleton loading -->
|
||||
<div
|
||||
data-live-component="product-list"
|
||||
data-loading-type="skeleton"
|
||||
data-loading-fragments="product-list"
|
||||
>
|
||||
<div data-lc-fragment="product-list">
|
||||
<!-- Skeleton template will be shown during loading -->
|
||||
<div class="skeleton-item">
|
||||
<div class="skeleton-image"></div>
|
||||
<div class="skeleton-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Server-Side Loading Configuration
|
||||
|
||||
```php
|
||||
use App\Framework\LiveComponents\Attributes\Action;
|
||||
use App\Framework\LiveComponents\Attributes\Loading;
|
||||
|
||||
final class ProductList extends LiveComponent
|
||||
{
|
||||
#[Action]
|
||||
#[Loading(type: 'skeleton', fragments: ['product-list'], showDelay: 150)]
|
||||
public function loadProducts(string $category): void
|
||||
{
|
||||
$this->products = $this->productService->getByCategory($category);
|
||||
}
|
||||
|
||||
#[Action]
|
||||
#[Loading(type: 'spinner', showDelay: 0)] // Show immediately
|
||||
public function quickAction(): void
|
||||
{
|
||||
// Fast action with immediate spinner
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Loading Types
|
||||
|
||||
#### 1. Skeleton Loading
|
||||
|
||||
```html
|
||||
<!-- Skeleton template in component -->
|
||||
<div data-lc-fragment="content">
|
||||
<!-- Default content -->
|
||||
<div class="product-card">
|
||||
<img src="{product.image}" />
|
||||
<h3>{product.name}</h3>
|
||||
</div>
|
||||
|
||||
<!-- Skeleton template (shown during loading) -->
|
||||
<template data-skeleton-template>
|
||||
<div class="product-card skeleton">
|
||||
<div class="skeleton-image"></div>
|
||||
<div class="skeleton-text"></div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 2. Spinner Loading
|
||||
|
||||
```html
|
||||
<!-- Automatic spinner overlay -->
|
||||
<div
|
||||
data-live-component="component-id"
|
||||
data-loading-type="spinner"
|
||||
>
|
||||
<!-- Spinner automatically appears during actions -->
|
||||
</div>
|
||||
```
|
||||
|
||||
#### 3. Progress Loading
|
||||
|
||||
```html
|
||||
<!-- Progress bar for long-running actions -->
|
||||
<div
|
||||
data-live-component="upload-component"
|
||||
data-loading-type="progress"
|
||||
>
|
||||
<div class="progress-bar" data-progress="0"></div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Custom Loading Configuration
|
||||
|
||||
```javascript
|
||||
// Configure loading behavior per component
|
||||
const component = LiveComponentManager.getInstance().getComponent('component-id');
|
||||
|
||||
component.setLoadingConfig({
|
||||
type: 'skeleton',
|
||||
fragments: ['content', 'sidebar'],
|
||||
showDelay: 150,
|
||||
hideDelay: 100
|
||||
});
|
||||
```
|
||||
|
||||
### Loading Events
|
||||
|
||||
```javascript
|
||||
// Listen for loading state changes
|
||||
window.addEventListener('livecomponent:loading-started', (e) => {
|
||||
const { componentId, type, fragments } = e.detail;
|
||||
console.log(`Loading started: ${componentId} (${type})`);
|
||||
});
|
||||
|
||||
window.addEventListener('livecomponent:loading-finished', (e) => {
|
||||
const { componentId, duration } = e.detail;
|
||||
console.log(`Loading finished: ${componentId} (${duration}ms)`);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UI Helper System
|
||||
|
||||
### Overview
|
||||
|
||||
The UI Helper System provides a standardized way for LiveComponents to interact with common UI elements like dialogs, modals, and notifications.
|
||||
|
||||
**Features**:
|
||||
- Unified API for UI components
|
||||
- Integration with UIManager
|
||||
- Promise-based API
|
||||
- Automatic cleanup
|
||||
- Component-scoped UI elements
|
||||
|
||||
### Dialog & Modal Helpers
|
||||
|
||||
```php
|
||||
use App\Framework\LiveComponents\Attributes\Action;
|
||||
|
||||
final class UserManagement extends LiveComponent
|
||||
{
|
||||
#[Action]
|
||||
public function showDeleteConfirm(int $userId): void
|
||||
{
|
||||
// Show confirmation dialog via UI Helper
|
||||
$this->uiHelper->showDialog(
|
||||
title: 'Delete User',
|
||||
message: 'Are you sure you want to delete this user?',
|
||||
buttons: [
|
||||
['label' => 'Cancel', 'action' => 'cancel'],
|
||||
['label' => 'Delete', 'action' => 'confirm', 'variant' => 'danger']
|
||||
]
|
||||
)->then(function($action) use ($userId) {
|
||||
if ($action === 'confirm') {
|
||||
$this->deleteUser($userId);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### JavaScript API
|
||||
|
||||
```javascript
|
||||
import { LiveComponentUIHelper } from './modules/livecomponent/LiveComponentUIHelper.js';
|
||||
|
||||
const uiHelper = new LiveComponentUIHelper(liveComponentManager);
|
||||
|
||||
// Show modal
|
||||
const action = await uiHelper.showModal('component-id', {
|
||||
title: 'Confirm Action',
|
||||
content: '<p>Are you sure?</p>',
|
||||
size: 'medium',
|
||||
buttons: [
|
||||
{ label: 'Cancel', action: 'cancel' },
|
||||
{ label: 'Confirm', action: 'confirm', variant: 'primary' }
|
||||
]
|
||||
});
|
||||
|
||||
if (action === 'confirm') {
|
||||
// Handle confirmation
|
||||
}
|
||||
|
||||
// Show alert
|
||||
await uiHelper.showAlert('component-id', {
|
||||
title: 'Success',
|
||||
message: 'Operation completed successfully',
|
||||
type: 'success'
|
||||
});
|
||||
|
||||
// Show confirm dialog
|
||||
const confirmed = await uiHelper.showConfirm('component-id', {
|
||||
title: 'Delete Item',
|
||||
message: 'This action cannot be undone.',
|
||||
confirmText: 'Delete',
|
||||
cancelText: 'Cancel'
|
||||
});
|
||||
```
|
||||
|
||||
### Notification Helpers
|
||||
|
||||
```javascript
|
||||
// Show notification
|
||||
uiHelper.showNotification('component-id', 'Operation successful', 'success');
|
||||
|
||||
// Show error notification with retry
|
||||
uiHelper.showErrorNotification(
|
||||
'component-id',
|
||||
'Upload failed. Please try again.',
|
||||
'error',
|
||||
true, // Can retry
|
||||
() => {
|
||||
// Retry logic
|
||||
retryUpload();
|
||||
}
|
||||
);
|
||||
|
||||
// Hide notification
|
||||
uiHelper.hideNotification('component-id');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notification Component
|
||||
|
||||
### Overview
|
||||
|
||||
The NotificationComponent is a full-featured LiveComponent for displaying toast notifications with support for different types, positions, durations, and action buttons.
|
||||
|
||||
**Features**:
|
||||
- Type-safe state management
|
||||
- Multiple notification types (info, success, warning, error)
|
||||
- Configurable positions (top-right, top-left, bottom-right, bottom-left)
|
||||
- Auto-dismiss with duration
|
||||
- Action buttons
|
||||
- Icon support
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```php
|
||||
use App\Application\LiveComponents\Notification\NotificationComponent;
|
||||
use App\Framework\LiveComponents\ValueObjects\ComponentId;
|
||||
|
||||
final class ProductController
|
||||
{
|
||||
public function create(): ViewResult
|
||||
{
|
||||
$notification = NotificationComponent::mount(
|
||||
ComponentId::generate('notification'),
|
||||
message: 'Product created successfully',
|
||||
type: 'success',
|
||||
duration: 5000
|
||||
);
|
||||
|
||||
return new ViewResult('product/index', [
|
||||
'notification' => $notification
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Template Integration
|
||||
|
||||
```html
|
||||
<!-- Include notification component in layout -->
|
||||
{notification}
|
||||
|
||||
<!-- Or use in component template -->
|
||||
<div data-live-component="{notification.id}">
|
||||
<!-- Notification will render here -->
|
||||
</div>
|
||||
```
|
||||
|
||||
### Server-Side Actions
|
||||
|
||||
```php
|
||||
final class NotificationExample extends LiveComponent
|
||||
{
|
||||
#[Action]
|
||||
public function showSuccess(): NotificationState
|
||||
{
|
||||
return NotificationState::empty()
|
||||
->withMessage('Operation successful!', 'success')
|
||||
->show();
|
||||
}
|
||||
|
||||
#[Action]
|
||||
public function showError(string $message): NotificationState
|
||||
{
|
||||
return NotificationState::empty()
|
||||
->withMessage($message, 'error')
|
||||
->show();
|
||||
}
|
||||
|
||||
#[Action]
|
||||
public function showWithAction(): NotificationState
|
||||
{
|
||||
return new NotificationState(
|
||||
message: 'File uploaded successfully',
|
||||
type: 'success',
|
||||
isVisible: true,
|
||||
actionText: 'View',
|
||||
actionUrl: '/files'
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Client-Side API
|
||||
|
||||
```javascript
|
||||
// Show notification via LiveComponent action
|
||||
liveComponentManager.executeAction('notification-id', 'showNotification', {
|
||||
message: 'Operation successful',
|
||||
type: 'success',
|
||||
duration: 5000
|
||||
});
|
||||
|
||||
// Hide notification
|
||||
liveComponentManager.executeAction('notification-id', 'hide');
|
||||
```
|
||||
|
||||
### Notification Types
|
||||
|
||||
```php
|
||||
// Info notification
|
||||
$notification = NotificationState::empty()
|
||||
->withMessage('New update available', 'info')
|
||||
->show();
|
||||
|
||||
// Success notification
|
||||
$notification = NotificationState::empty()
|
||||
->withMessage('Changes saved', 'success')
|
||||
->show();
|
||||
|
||||
// Warning notification
|
||||
$notification = NotificationState::empty()
|
||||
->withMessage('Low disk space', 'warning')
|
||||
->show();
|
||||
|
||||
// Error notification
|
||||
$notification = NotificationState::empty()
|
||||
->withMessage('Upload failed', 'error')
|
||||
->show();
|
||||
```
|
||||
|
||||
### Notification Positions
|
||||
|
||||
```php
|
||||
$notification = new NotificationState(
|
||||
message: 'Notification message',
|
||||
type: 'info',
|
||||
position: 'top-right', // or 'top-left', 'bottom-right', 'bottom-left'
|
||||
isVisible: true
|
||||
);
|
||||
```
|
||||
|
||||
### Notification with Action Button
|
||||
|
||||
```php
|
||||
$notification = new NotificationState(
|
||||
message: 'File ready for download',
|
||||
type: 'success',
|
||||
isVisible: true,
|
||||
actionText: 'Download',
|
||||
actionUrl: '/download/file.pdf'
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dialog & Modal Integration
|
||||
|
||||
### Overview
|
||||
|
||||
LiveComponents integrate seamlessly with the UIManager for dialogs and modals, providing a consistent API across the application.
|
||||
|
||||
### Basic Modal Usage
|
||||
|
||||
```php
|
||||
use App\Framework\LiveComponents\Attributes\Action;
|
||||
|
||||
final class UserSettings extends LiveComponent
|
||||
{
|
||||
#[Action]
|
||||
public function showEditModal(int $userId): void
|
||||
{
|
||||
// Modal will be shown via UI Helper
|
||||
$this->uiHelper->showModal(
|
||||
title: 'Edit User',
|
||||
content: $this->renderEditForm($userId),
|
||||
size: 'large',
|
||||
buttons: [
|
||||
['label' => 'Save', 'action' => 'save', 'variant' => 'primary'],
|
||||
['label' => 'Cancel', 'action' => 'cancel']
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Modal with LiveComponent Content
|
||||
|
||||
```php
|
||||
#[Action]
|
||||
public function showUserModal(int $userId): void
|
||||
{
|
||||
$userComponent = UserDetailsComponent::mount(
|
||||
ComponentId::generate('user-details'),
|
||||
userId: $userId
|
||||
);
|
||||
|
||||
$this->uiHelper->showModal(
|
||||
title: 'User Details',
|
||||
content: $userComponent->render(),
|
||||
size: 'medium'
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Modal Events
|
||||
|
||||
```javascript
|
||||
// Listen for modal events
|
||||
window.addEventListener('livecomponent:modal-opened', (e) => {
|
||||
const { componentId, modalInstance } = e.detail;
|
||||
console.log(`Modal opened for component: ${componentId}`);
|
||||
});
|
||||
|
||||
window.addEventListener('livecomponent:modal-closed', (e) => {
|
||||
const { componentId, action } = e.detail;
|
||||
console.log(`Modal closed with action: ${action}`);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Tooltip Usage
|
||||
|
||||
- **Do**: Use tooltips for helpful context and validation errors
|
||||
- **Don't**: Overuse tooltips - they can be distracting
|
||||
- **Accessibility**: Always ensure tooltips are keyboard-accessible
|
||||
|
||||
```html
|
||||
<!-- Good: Helpful tooltip -->
|
||||
<button data-tooltip="Save your changes (Ctrl+S)">
|
||||
Save
|
||||
</button>
|
||||
|
||||
<!-- Bad: Obvious tooltip -->
|
||||
<button data-tooltip="Click to save">
|
||||
Save
|
||||
</button>
|
||||
```
|
||||
|
||||
### 2. Loading States
|
||||
|
||||
- **Do**: Use skeleton loading for content-heavy updates
|
||||
- **Do**: Use spinners for quick actions (< 500ms)
|
||||
- **Don't**: Show loading for optimistic UI updates
|
||||
- **Do**: Configure appropriate delays to prevent flickering
|
||||
|
||||
```php
|
||||
// Good: Appropriate loading type
|
||||
#[Loading(type: 'skeleton', fragments: ['product-list'])]
|
||||
public function loadProducts(): void { }
|
||||
|
||||
// Good: Quick action with spinner
|
||||
#[Loading(type: 'spinner', showDelay: 0)]
|
||||
public function toggleFavorite(): void { }
|
||||
```
|
||||
|
||||
### 3. Notifications
|
||||
|
||||
- **Do**: Use notifications for important feedback
|
||||
- **Don't**: Overuse notifications - they can be annoying
|
||||
- **Do**: Set appropriate durations (5s for success, longer for errors)
|
||||
- **Do**: Provide action buttons for actionable notifications
|
||||
|
||||
```php
|
||||
// Good: Clear, actionable notification
|
||||
$notification = new NotificationState(
|
||||
message: 'File uploaded successfully',
|
||||
type: 'success',
|
||||
duration: 5000,
|
||||
actionText: 'View',
|
||||
actionUrl: '/files'
|
||||
);
|
||||
|
||||
// Bad: Too many notifications
|
||||
// Don't show a notification for every minor action
|
||||
```
|
||||
|
||||
### 4. Modals & Dialogs
|
||||
|
||||
- **Do**: Use modals for important confirmations
|
||||
- **Don't**: Overuse modals - they interrupt user flow
|
||||
- **Do**: Provide clear action buttons
|
||||
- **Do**: Support keyboard navigation (Escape to close)
|
||||
|
||||
```php
|
||||
// Good: Clear confirmation dialog
|
||||
$this->uiHelper->showConfirm(
|
||||
title: 'Delete Item',
|
||||
message: 'This action cannot be undone.',
|
||||
confirmText: 'Delete',
|
||||
cancelText: 'Cancel'
|
||||
);
|
||||
```
|
||||
|
||||
### 5. Error Handling
|
||||
|
||||
- **Do**: Use ErrorBoundary for automatic error handling
|
||||
- **Do**: Show user-friendly error messages
|
||||
- **Do**: Provide retry options for recoverable errors
|
||||
- **Don't**: Show technical error details to users
|
||||
|
||||
```php
|
||||
// Good: User-friendly error
|
||||
throw LiveComponentError::validation(
|
||||
'Please enter a valid email address',
|
||||
['field' => 'email'],
|
||||
$this->id->toString()
|
||||
);
|
||||
|
||||
// Bad: Technical error
|
||||
throw new \Exception('Invalid email format: ' . $email);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Global Configuration
|
||||
|
||||
```javascript
|
||||
import { sharedConfig } from './modules/livecomponent/SharedConfig.js';
|
||||
|
||||
// Configure default values
|
||||
sharedConfig.defaultDebounce = 300;
|
||||
sharedConfig.defaultCacheTTL = 5000;
|
||||
sharedConfig.defaultLoadingShowDelay = 150;
|
||||
sharedConfig.defaultLoadingType = 'skeleton';
|
||||
sharedConfig.defaultNotificationDuration = 5000;
|
||||
sharedConfig.defaultNotificationPosition = 'top-right';
|
||||
sharedConfig.defaultModalAnimation = 'fade';
|
||||
```
|
||||
|
||||
### Per-Component Configuration
|
||||
|
||||
```html
|
||||
<!-- Component-level configuration -->
|
||||
<div
|
||||
data-live-component="component-id"
|
||||
data-loading-type="skeleton"
|
||||
data-loading-show-delay="200"
|
||||
data-notification-position="bottom-right"
|
||||
>
|
||||
<!-- Component content -->
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Tooltips Not Showing
|
||||
|
||||
1. Check that `data-tooltip` attribute is present
|
||||
2. Verify TooltipManager is initialized
|
||||
3. Check browser console for errors
|
||||
4. Ensure element is visible and not hidden
|
||||
|
||||
### Loading States Not Working
|
||||
|
||||
1. Verify `data-loading-type` attribute is set
|
||||
2. Check that fragments match between HTML and PHP
|
||||
3. Ensure LoadingStateManager is initialized
|
||||
4. Check for JavaScript errors in console
|
||||
|
||||
### Notifications Not Displaying
|
||||
|
||||
1. Verify NotificationComponent is mounted
|
||||
2. Check that component ID matches
|
||||
3. Ensure state is properly serialized
|
||||
4. Check browser console for errors
|
||||
|
||||
### Modals Not Opening
|
||||
|
||||
1. Verify UIManager is initialized
|
||||
2. Check that modal content is valid HTML
|
||||
3. Ensure no JavaScript errors are blocking execution
|
||||
4. Check z-index conflicts
|
||||
|
||||
---
|
||||
|
||||
**Next**: [API Reference](api-reference.md) →
|
||||
|
||||
340
docs/modules/analytics.md
Normal file
340
docs/modules/analytics.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# Analytics Module
|
||||
|
||||
**Unified Analytics System for Event Tracking and User Behavior**
|
||||
|
||||
The Analytics Module provides a comprehensive analytics system with GDPR compliance and multiple provider support.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Event Tracking** - Track custom events
|
||||
- **Page View Tracking** - Automatic and manual page view tracking
|
||||
- **User Behavior Tracking** - Track user interactions
|
||||
- **Multiple Providers** - Support for Google Analytics, custom endpoints, and more
|
||||
- **GDPR Compliance** - Consent management and data anonymization
|
||||
- **Integration with LiveComponents** - Automatic tracking of LiveComponent events
|
||||
- **User Identification** - Identify users for user-level analytics
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { Analytics } from './modules/analytics/index.js';
|
||||
|
||||
// Create analytics instance
|
||||
const analytics = Analytics.create({
|
||||
providers: ['google-analytics'],
|
||||
gdprCompliant: true,
|
||||
requireConsent: true
|
||||
});
|
||||
|
||||
// Give consent (GDPR)
|
||||
analytics.giveConsent();
|
||||
|
||||
// Track event
|
||||
await analytics.track('button_click', {
|
||||
button_id: 'submit',
|
||||
page: '/contact'
|
||||
});
|
||||
|
||||
// Track page view
|
||||
await analytics.trackPageView('/dashboard');
|
||||
```
|
||||
|
||||
### Module System Integration
|
||||
|
||||
```html
|
||||
<!-- Enable global analytics -->
|
||||
<script type="module">
|
||||
import { init } from './modules/analytics/index.js';
|
||||
|
||||
init({
|
||||
providers: [
|
||||
{
|
||||
type: 'google-analytics',
|
||||
measurementId: 'G-XXXXXXXXXX'
|
||||
},
|
||||
{
|
||||
type: 'custom',
|
||||
endpoint: '/api/analytics'
|
||||
}
|
||||
],
|
||||
gdprCompliant: true,
|
||||
requireConsent: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Access globally -->
|
||||
<script>
|
||||
// Give consent
|
||||
window.Analytics.giveConsent();
|
||||
|
||||
// Track event
|
||||
window.Analytics.track('purchase', {
|
||||
value: 99.99,
|
||||
currency: 'EUR'
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### Analytics.create(config)
|
||||
|
||||
Create a new Analytics instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.enabled` - Enable analytics (default: true)
|
||||
- `config.providers` - Array of provider configs
|
||||
- `config.gdprCompliant` - Enable GDPR compliance (default: true)
|
||||
- `config.requireConsent` - Require user consent (default: false)
|
||||
- `config.anonymizeIp` - Anonymize IP addresses (default: true)
|
||||
|
||||
### analytics.track(eventName, properties)
|
||||
|
||||
Track a custom event.
|
||||
|
||||
**Parameters**:
|
||||
- `eventName` - Event name
|
||||
- `properties` - Event properties
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
await analytics.track('purchase', {
|
||||
value: 99.99,
|
||||
currency: 'EUR',
|
||||
items: [{ id: 'product-1', quantity: 1 }]
|
||||
});
|
||||
```
|
||||
|
||||
### analytics.trackPageView(path, properties)
|
||||
|
||||
Track a page view.
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Page path (optional, defaults to current path)
|
||||
- `properties` - Additional properties
|
||||
|
||||
### analytics.identify(userId, traits)
|
||||
|
||||
Identify a user.
|
||||
|
||||
**Parameters**:
|
||||
- `userId` - User ID
|
||||
- `traits` - User traits (name, email, etc.)
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
await analytics.identify('user-123', {
|
||||
name: 'John Doe',
|
||||
email: 'john@example.com'
|
||||
});
|
||||
```
|
||||
|
||||
### analytics.trackBehavior(action, target, properties)
|
||||
|
||||
Track user behavior.
|
||||
|
||||
**Parameters**:
|
||||
- `action` - Action type (click, scroll, etc.)
|
||||
- `target` - Target element or identifier
|
||||
- `properties` - Additional properties
|
||||
|
||||
### analytics.giveConsent()
|
||||
|
||||
Give GDPR consent.
|
||||
|
||||
### analytics.revokeConsent()
|
||||
|
||||
Revoke GDPR consent.
|
||||
|
||||
---
|
||||
|
||||
## Providers
|
||||
|
||||
### Google Analytics
|
||||
|
||||
```javascript
|
||||
const analytics = Analytics.create({
|
||||
providers: [
|
||||
{
|
||||
type: 'google-analytics',
|
||||
measurementId: 'G-XXXXXXXXXX'
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Provider
|
||||
|
||||
```javascript
|
||||
const analytics = Analytics.create({
|
||||
providers: [
|
||||
{
|
||||
type: 'custom',
|
||||
endpoint: '/api/analytics'
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Multiple Providers
|
||||
|
||||
```javascript
|
||||
const analytics = Analytics.create({
|
||||
providers: [
|
||||
'google-analytics',
|
||||
{
|
||||
type: 'custom',
|
||||
endpoint: '/api/analytics'
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## GDPR Compliance
|
||||
|
||||
### Consent Management
|
||||
|
||||
```javascript
|
||||
const analytics = Analytics.create({
|
||||
requireConsent: true,
|
||||
gdprCompliant: true
|
||||
});
|
||||
|
||||
// Show consent banner
|
||||
showConsentBanner(() => {
|
||||
analytics.giveConsent();
|
||||
});
|
||||
```
|
||||
|
||||
### Data Anonymization
|
||||
|
||||
```javascript
|
||||
// IP addresses are automatically anonymized
|
||||
// PII fields are automatically removed
|
||||
await analytics.track('event', {
|
||||
email: 'user@example.com', // Will be removed
|
||||
ip: '192.168.1.1' // Will be anonymized to 192.168.1.0
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with LiveComponents
|
||||
|
||||
```javascript
|
||||
import { Analytics } from './modules/analytics/index.js';
|
||||
import { LiveComponent } from './modules/livecomponent/index.js';
|
||||
|
||||
const analytics = Analytics.create();
|
||||
|
||||
// Track LiveComponent actions
|
||||
LiveComponent.on('action-executed', (componentId, actionName, params) => {
|
||||
analytics.track('livecomponent:action', {
|
||||
component_id: componentId,
|
||||
action: actionName,
|
||||
params
|
||||
});
|
||||
});
|
||||
|
||||
// Track component updates
|
||||
LiveComponent.on('component-updated', (componentId) => {
|
||||
analytics.track('livecomponent:updated', {
|
||||
component_id: componentId
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### E-commerce Tracking
|
||||
|
||||
```javascript
|
||||
// Track purchase
|
||||
await analytics.track('purchase', {
|
||||
value: 99.99,
|
||||
currency: 'EUR',
|
||||
items: [
|
||||
{ id: 'product-1', name: 'Product 1', price: 49.99, quantity: 1 },
|
||||
{ id: 'product-2', name: 'Product 2', price: 50.00, quantity: 1 }
|
||||
]
|
||||
});
|
||||
|
||||
// Track add to cart
|
||||
await analytics.track('add_to_cart', {
|
||||
product_id: 'product-1',
|
||||
value: 49.99
|
||||
});
|
||||
```
|
||||
|
||||
### User Behavior Tracking
|
||||
|
||||
```javascript
|
||||
// Track button clicks
|
||||
document.addEventListener('click', (event) => {
|
||||
if (event.target.matches('[data-track]')) {
|
||||
analytics.trackBehavior('click', event.target.id, {
|
||||
text: event.target.textContent
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Track form submissions
|
||||
document.addEventListener('submit', (event) => {
|
||||
analytics.track('form_submit', {
|
||||
form_id: event.target.id,
|
||||
form_name: event.target.name
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Page View Tracking
|
||||
|
||||
```javascript
|
||||
// Automatic tracking on navigation
|
||||
// Or manual tracking
|
||||
await analytics.trackPageView('/dashboard', {
|
||||
section: 'admin',
|
||||
user_role: 'admin'
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Respect User Privacy** - Always get consent before tracking
|
||||
2. **Anonymize Data** - Remove PII and anonymize IPs
|
||||
3. **Track Meaningful Events** - Focus on business-critical events
|
||||
4. **Use Consistent Naming** - Use consistent event names
|
||||
5. **Monitor Performance** - Don't let analytics slow down the app
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- Fetch API
|
||||
- CustomEvent support
|
||||
|
||||
---
|
||||
|
||||
**Next**: Continue with remaining modules →
|
||||
|
||||
180
docs/modules/animation-system-migration.md
Normal file
180
docs/modules/animation-system-migration.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Animation System Migration Guide
|
||||
|
||||
**Complete Migration Guide from Old Scroll Animation Modules to Unified Animation System**
|
||||
|
||||
This guide helps you migrate from the old scroll animation modules to the new unified Animation System.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Animation System consolidates 8 separate modules into one unified system:
|
||||
|
||||
- `scrollfx` → `AnimationSystem` (fade-in, zoom-in)
|
||||
- `parallax` → `AnimationSystem` (parallax)
|
||||
- `scroll-timeline` → `AnimationSystem` (timeline)
|
||||
- `scroll-loop` → `AnimationSystem` (timeline with loop)
|
||||
- `scroll-dependent` → `AnimationSystem` (dependent animations)
|
||||
- `sticky-fade` → `AnimationSystem` (sticky-fade)
|
||||
- `sticky-steps` → `AnimationSystem` (sticky-steps)
|
||||
- `smooth-scroll` → Keep separate (different purpose)
|
||||
|
||||
---
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### 1. Update Imports
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
import { createTrigger } from './modules/scrollfx/index.js';
|
||||
import { init as initParallax } from './modules/parallax/index.js';
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
import { AnimationSystem } from './modules/animation-system/index.js';
|
||||
```
|
||||
|
||||
### 2. Update Initialization
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
createTrigger({ element: '.fade-in', offset: 0.85 });
|
||||
initParallax({ selector: '.parallax' });
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
const system = AnimationSystem.create();
|
||||
// Auto-initializes based on HTML attributes
|
||||
// Or manually:
|
||||
document.querySelectorAll('.fade-in').forEach(el => {
|
||||
system.registerAnimation(el, { type: 'fade-in', offset: 0.85 });
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Update HTML Attributes
|
||||
|
||||
Most HTML attributes remain compatible, but you can use new unified attributes:
|
||||
|
||||
**Before**:
|
||||
```html
|
||||
<div class="fade-in-on-scroll" data-offset="0.85">Content</div>
|
||||
<div class="parallax" data-speed="0.5">Content</div>
|
||||
```
|
||||
|
||||
**After** (still works, or use new format):
|
||||
```html
|
||||
<div data-animate="fade-in" data-offset="0.85">Content</div>
|
||||
<div data-parallax data-speed="0.5">Content</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Module-Specific Migrations
|
||||
|
||||
### scrollfx → AnimationSystem
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
import { createTrigger } from './modules/scrollfx/index.js';
|
||||
createTrigger({
|
||||
element: '.fade-in',
|
||||
offset: 0.85,
|
||||
baseDelay: 0.05,
|
||||
once: true
|
||||
});
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
import { AnimationSystem } from './modules/animation-system/index.js';
|
||||
const system = AnimationSystem.create();
|
||||
document.querySelectorAll('.fade-in').forEach((el, index) => {
|
||||
system.registerAnimation(el, {
|
||||
type: 'fade-in',
|
||||
offset: 0.85,
|
||||
delay: index * 0.05,
|
||||
once: true
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### parallax → AnimationSystem
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
import { init } from './modules/parallax/index.js';
|
||||
init({ selector: '.parallax', speed: 0.5 });
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
import { AnimationSystem } from './modules/animation-system/index.js';
|
||||
const system = AnimationSystem.create();
|
||||
document.querySelectorAll('.parallax').forEach(el => {
|
||||
system.registerAnimation(el, {
|
||||
type: 'parallax',
|
||||
speed: 0.5
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### scroll-timeline → AnimationSystem
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
import { init } from './modules/scroll-timeline/index.js';
|
||||
init({ attribute: 'data-scroll-step', triggerPoint: 0.4 });
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
import { AnimationSystem } from './modules/animation-system/index.js';
|
||||
const system = AnimationSystem.create();
|
||||
document.querySelectorAll('[data-scroll-step]').forEach(el => {
|
||||
system.registerAnimation(el, {
|
||||
type: 'timeline',
|
||||
steps: parseInt(el.dataset.scrollSteps) || null,
|
||||
triggerPoint: 0.4
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
The Animation System maintains backward compatibility with existing HTML:
|
||||
|
||||
- Old CSS classes still work: `.fade-in-on-scroll`, `.zoom-in`, `.parallax`
|
||||
- Old data attributes still work: `data-parallax`, `data-scroll-step`, etc.
|
||||
- Old module initialization still works (but deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
1. **Module Exports** - Old module exports are deprecated
|
||||
2. **JavaScript API** - Some APIs have changed (see migration examples)
|
||||
3. **Configuration** - Some config options have been renamed
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] All fade-in animations work
|
||||
- [ ] All parallax effects work
|
||||
- [ ] All timeline animations work
|
||||
- [ ] All sticky animations work
|
||||
- [ ] Performance is acceptable
|
||||
- [ ] No console errors
|
||||
- [ ] Backward compatibility maintained
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions, see the main [Animation System documentation](animation-system.md).
|
||||
|
||||
270
docs/modules/animation-system.md
Normal file
270
docs/modules/animation-system.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Animation System Module
|
||||
|
||||
**Unified Animation System for Scroll-Based Animations**
|
||||
|
||||
The Animation System Module consolidates all scroll animation modules into a single, unified system with backward compatibility.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Fade-In Animations** - Elements fade in when scrolling into view
|
||||
- **Zoom-In Animations** - Elements zoom in when scrolling into view
|
||||
- **Parallax Effects** - Parallax scrolling effects
|
||||
- **Scroll Timeline** - Step-based scroll animations
|
||||
- **Sticky Fade** - Fade effects on sticky elements
|
||||
- **Sticky Steps** - Step-based animations on sticky elements
|
||||
- **IntersectionObserver Support** - Efficient scroll detection
|
||||
- **Backward Compatibility** - Works with existing HTML attributes
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { AnimationSystem } from './modules/animation-system/index.js';
|
||||
|
||||
// Create animation system
|
||||
const system = AnimationSystem.create({
|
||||
enabled: true,
|
||||
useIntersectionObserver: true
|
||||
});
|
||||
|
||||
// Register animation
|
||||
system.registerAnimation(element, {
|
||||
type: 'fade-in',
|
||||
offset: 0.85,
|
||||
delay: 0.1,
|
||||
once: true
|
||||
});
|
||||
```
|
||||
|
||||
### Module System Integration
|
||||
|
||||
```html
|
||||
<!-- Enable global animation system -->
|
||||
<script type="module">
|
||||
import { init } from './modules/animation-system/index.js';
|
||||
|
||||
init({
|
||||
enabled: true,
|
||||
useIntersectionObserver: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Use data attributes (auto-initialized) -->
|
||||
<div class="fade-in-on-scroll" data-offset="0.85" data-delay="0.1">
|
||||
Content that fades in
|
||||
</div>
|
||||
|
||||
<div data-parallax data-speed="0.5">
|
||||
Parallax content
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### AnimationSystem.create(config)
|
||||
|
||||
Create a new AnimationSystem instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.enabled` - Enable animations (default: true)
|
||||
- `config.useIntersectionObserver` - Use IntersectionObserver (default: true)
|
||||
- `config.throttleDelay` - Throttle delay for scroll handler (default: 16ms)
|
||||
|
||||
### system.registerAnimation(element, config)
|
||||
|
||||
Register an animation for an element.
|
||||
|
||||
**Parameters**:
|
||||
- `element` - HTMLElement
|
||||
- `config.type` - Animation type
|
||||
- `config.offset` - Trigger offset (0-1)
|
||||
- `config.delay` - Animation delay
|
||||
- `config.once` - Trigger only once
|
||||
- `config.speed` - Parallax speed
|
||||
- `config.steps` - Number of steps
|
||||
- `config.triggerPoint` - Trigger point (0-1)
|
||||
|
||||
---
|
||||
|
||||
## Animation Types
|
||||
|
||||
### Fade-In
|
||||
|
||||
```javascript
|
||||
system.registerAnimation(element, {
|
||||
type: 'fade-in',
|
||||
offset: 0.85,
|
||||
delay: 0.1,
|
||||
once: true
|
||||
});
|
||||
```
|
||||
|
||||
### Zoom-In
|
||||
|
||||
```javascript
|
||||
system.registerAnimation(element, {
|
||||
type: 'zoom-in',
|
||||
offset: 0.85,
|
||||
delay: 0.1
|
||||
});
|
||||
```
|
||||
|
||||
### Parallax
|
||||
|
||||
```javascript
|
||||
system.registerAnimation(element, {
|
||||
type: 'parallax',
|
||||
speed: 0.5
|
||||
});
|
||||
```
|
||||
|
||||
### Timeline
|
||||
|
||||
```javascript
|
||||
system.registerAnimation(element, {
|
||||
type: 'timeline',
|
||||
steps: 5,
|
||||
triggerPoint: 0.4
|
||||
});
|
||||
```
|
||||
|
||||
### Sticky Fade
|
||||
|
||||
```javascript
|
||||
system.registerAnimation(element, {
|
||||
type: 'sticky-fade',
|
||||
fadeStart: 0,
|
||||
fadeEnd: 1
|
||||
});
|
||||
```
|
||||
|
||||
### Sticky Steps
|
||||
|
||||
```javascript
|
||||
system.registerAnimation(element, {
|
||||
type: 'sticky-steps',
|
||||
steps: 3
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HTML Data Attributes
|
||||
|
||||
### Auto-Initialization
|
||||
|
||||
The system automatically initializes animations based on HTML attributes:
|
||||
|
||||
```html
|
||||
<!-- Fade in -->
|
||||
<div class="fade-in-on-scroll" data-offset="0.85" data-delay="0.1">
|
||||
Content
|
||||
</div>
|
||||
|
||||
<!-- Parallax -->
|
||||
<div data-parallax data-speed="0.5">
|
||||
Parallax content
|
||||
</div>
|
||||
|
||||
<!-- Timeline -->
|
||||
<div data-scroll-timeline data-scroll-steps="5">
|
||||
Timeline content
|
||||
</div>
|
||||
|
||||
<!-- Sticky fade -->
|
||||
<div data-sticky-fade data-fade-start="0" data-fade-end="1">
|
||||
Sticky content
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
The system maintains backward compatibility with old modules:
|
||||
|
||||
```html
|
||||
<!-- Old scrollfx classes still work -->
|
||||
<div class="fade-in-on-scroll">Content</div>
|
||||
<div class="zoom-in">Content</div>
|
||||
|
||||
<!-- Old parallax attributes still work -->
|
||||
<div class="parallax" data-speed="0.5">Content</div>
|
||||
|
||||
<!-- Old scroll-timeline attributes still work -->
|
||||
<div data-scroll-step="0">Content</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Guide
|
||||
|
||||
### From scrollfx
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
import { createTrigger } from './modules/scrollfx/index.js';
|
||||
createTrigger({ element: '.fade-in', offset: 0.85 });
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
import { AnimationSystem } from './modules/animation-system/index.js';
|
||||
const system = AnimationSystem.create();
|
||||
document.querySelectorAll('.fade-in').forEach(el => {
|
||||
system.registerAnimation(el, { type: 'fade-in', offset: 0.85 });
|
||||
});
|
||||
```
|
||||
|
||||
### From parallax
|
||||
|
||||
**Before**:
|
||||
```javascript
|
||||
import { init } from './modules/parallax/index.js';
|
||||
init({ selector: '.parallax' });
|
||||
```
|
||||
|
||||
**After**:
|
||||
```javascript
|
||||
import { AnimationSystem } from './modules/animation-system/index.js';
|
||||
const system = AnimationSystem.create();
|
||||
document.querySelectorAll('.parallax').forEach(el => {
|
||||
system.registerAnimation(el, { type: 'parallax', speed: 0.5 });
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use IntersectionObserver** - More efficient than scroll listeners
|
||||
2. **Set Appropriate Offsets** - Balance visibility with performance
|
||||
3. **Use Once for Performance** - Set `once: true` for elements that don't need to re-animate
|
||||
4. **Throttle Updates** - Use appropriate throttle delays
|
||||
5. **Clean Up** - Remove animations when elements are removed
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- IntersectionObserver (for efficient detection)
|
||||
- requestAnimationFrame (for smooth animations)
|
||||
|
||||
---
|
||||
|
||||
**Next**: Continue with remaining modules →
|
||||
|
||||
382
docs/modules/cache-manager.md
Normal file
382
docs/modules/cache-manager.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# Cache Manager Module
|
||||
|
||||
**Intelligent Caching for API Responses and Computed Values**
|
||||
|
||||
The Cache Manager Module provides a comprehensive caching system with multiple storage backends and caching strategies.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Memory Cache** - Fast in-memory caching
|
||||
- **IndexedDB Cache** - Persistent browser storage
|
||||
- **Cache Strategies** - Multiple caching strategies (cache-first, network-first, stale-while-revalidate, etc.)
|
||||
- **Cache Invalidation** - Pattern-based cache invalidation
|
||||
- **Cache Warming** - Preload cache values
|
||||
- **Cache Analytics** - Track cache performance
|
||||
- **Integration with RequestDeduplicator** - Works with LiveComponent request deduplication
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { CacheManager, CacheStrategy } from './modules/cache-manager/index.js';
|
||||
|
||||
// Create cache manager
|
||||
const cache = CacheManager.create({
|
||||
defaultStrategy: CacheStrategy.STALE_WHILE_REVALIDATE,
|
||||
defaultTTL: 3600000 // 1 hour
|
||||
});
|
||||
|
||||
// Get or set value
|
||||
const data = await cache.getOrSet('users', async () => {
|
||||
const response = await fetch('/api/users');
|
||||
return await response.json();
|
||||
});
|
||||
|
||||
// Get from cache
|
||||
const cached = await cache.get('users');
|
||||
|
||||
// Set in cache
|
||||
await cache.set('users', data, { ttl: 1800000 }); // 30 minutes
|
||||
```
|
||||
|
||||
### Module System Integration
|
||||
|
||||
```html
|
||||
<!-- Enable global cache manager -->
|
||||
<script type="module">
|
||||
import { init } from './modules/cache-manager/index.js';
|
||||
|
||||
init({
|
||||
defaultStrategy: 'stale-while-revalidate',
|
||||
enableIndexedDB: true
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Access globally -->
|
||||
<script>
|
||||
// Get or set
|
||||
const data = await window.CacheManager.getOrSet('key', async () => {
|
||||
return await fetch('/api/data').then(r => r.json());
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### CacheManager.create(config)
|
||||
|
||||
Create a new CacheManager instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.defaultStrategy` - Default caching strategy
|
||||
- `config.defaultTTL` - Default time-to-live in milliseconds
|
||||
- `config.maxMemorySize` - Maximum items in memory cache
|
||||
- `config.enableIndexedDB` - Enable IndexedDB persistence
|
||||
- `config.indexedDBName` - IndexedDB database name
|
||||
- `config.enableAnalytics` - Enable cache analytics
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const cache = CacheManager.create({
|
||||
defaultStrategy: CacheStrategy.STALE_WHILE_REVALIDATE,
|
||||
defaultTTL: 3600000,
|
||||
maxMemorySize: 100,
|
||||
enableIndexedDB: true
|
||||
});
|
||||
```
|
||||
|
||||
### cache.get(key, options)
|
||||
|
||||
Get value from cache.
|
||||
|
||||
**Parameters**:
|
||||
- `key` - Cache key
|
||||
- `options.strategy` - Caching strategy override
|
||||
- `options.ttl` - Time-to-live override
|
||||
|
||||
**Returns**: `Promise<any | null>`
|
||||
|
||||
### cache.set(key, value, options)
|
||||
|
||||
Set value in cache.
|
||||
|
||||
**Parameters**:
|
||||
- `key` - Cache key
|
||||
- `value` - Value to cache
|
||||
- `options.strategy` - Caching strategy
|
||||
- `options.ttl` - Time-to-live
|
||||
|
||||
**Returns**: `Promise<void>`
|
||||
|
||||
### cache.getOrSet(key, computeFn, options)
|
||||
|
||||
Get value from cache or compute and cache it.
|
||||
|
||||
**Parameters**:
|
||||
- `key` - Cache key
|
||||
- `computeFn` - Function to compute value if not cached
|
||||
- `options` - Cache options
|
||||
|
||||
**Returns**: `Promise<any>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const users = await cache.getOrSet('users', async () => {
|
||||
const response = await fetch('/api/users');
|
||||
return await response.json();
|
||||
}, { ttl: 1800000 });
|
||||
```
|
||||
|
||||
### cache.delete(key)
|
||||
|
||||
Delete value from cache.
|
||||
|
||||
**Parameters**:
|
||||
- `key` - Cache key
|
||||
|
||||
**Returns**: `Promise<void>`
|
||||
|
||||
### cache.clear()
|
||||
|
||||
Clear all cache.
|
||||
|
||||
**Returns**: `Promise<void>`
|
||||
|
||||
### cache.invalidate(pattern)
|
||||
|
||||
Invalidate cache entries matching a pattern.
|
||||
|
||||
**Parameters**:
|
||||
- `pattern` - String, RegExp, or function
|
||||
|
||||
**Returns**: `Promise<void>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
// Invalidate all user-related cache
|
||||
await cache.invalidate('user:');
|
||||
|
||||
// Invalidate with regex
|
||||
await cache.invalidate(/^user:\d+$/);
|
||||
|
||||
// Invalidate with function
|
||||
await cache.invalidate(key => key.startsWith('user:'));
|
||||
```
|
||||
|
||||
### cache.warm(keys, computeFn)
|
||||
|
||||
Warm cache by preloading values.
|
||||
|
||||
**Parameters**:
|
||||
- `keys` - Array of cache keys
|
||||
- `computeFn` - Function to compute value for each key
|
||||
|
||||
**Returns**: `Promise<void>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
await cache.warm(['user:1', 'user:2', 'user:3'], async (key) => {
|
||||
const userId = key.split(':')[1];
|
||||
const response = await fetch(`/api/users/${userId}`);
|
||||
return await response.json();
|
||||
});
|
||||
```
|
||||
|
||||
### cache.getAnalytics()
|
||||
|
||||
Get cache performance analytics.
|
||||
|
||||
**Returns**: `CacheAnalytics`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const analytics = cache.getAnalytics();
|
||||
console.log(`Hit rate: ${analytics.hitRate}%`);
|
||||
console.log(`Hits: ${analytics.hits}, Misses: ${analytics.misses}`);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cache Strategies
|
||||
|
||||
### Cache-First
|
||||
|
||||
Use cache if available, otherwise fetch.
|
||||
|
||||
```javascript
|
||||
const data = await cache.getOrSet('key', fetchData, {
|
||||
strategy: CacheStrategy.CACHE_FIRST
|
||||
});
|
||||
```
|
||||
|
||||
### Network-First
|
||||
|
||||
Try network first, fallback to cache.
|
||||
|
||||
```javascript
|
||||
const data = await cache.getOrSet('key', fetchData, {
|
||||
strategy: CacheStrategy.NETWORK_FIRST
|
||||
});
|
||||
```
|
||||
|
||||
### Stale-While-Revalidate
|
||||
|
||||
Return cache immediately, update in background.
|
||||
|
||||
```javascript
|
||||
const data = await cache.getOrSet('key', fetchData, {
|
||||
strategy: CacheStrategy.STALE_WHILE_REVALIDATE
|
||||
});
|
||||
```
|
||||
|
||||
### Network-Only
|
||||
|
||||
Always fetch from network, never use cache.
|
||||
|
||||
```javascript
|
||||
const data = await cache.getOrSet('key', fetchData, {
|
||||
strategy: CacheStrategy.NETWORK_ONLY
|
||||
});
|
||||
```
|
||||
|
||||
### Cache-Only
|
||||
|
||||
Only use cache, never fetch from network.
|
||||
|
||||
```javascript
|
||||
const data = await cache.get('key', {
|
||||
strategy: CacheStrategy.CACHE_ONLY
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with RequestDeduplicator
|
||||
|
||||
```javascript
|
||||
import { CacheManager } from './modules/cache-manager/index.js';
|
||||
import { RequestDeduplicator } from './modules/livecomponent/RequestDeduplicator.js';
|
||||
|
||||
const cache = CacheManager.create();
|
||||
const deduplicator = new RequestDeduplicator();
|
||||
|
||||
// Use cache with request deduplication
|
||||
async function fetchWithCache(url) {
|
||||
return await cache.getOrSet(url, async () => {
|
||||
// Check for pending request
|
||||
const pending = deduplicator.getPendingRequest('api', 'GET', { url });
|
||||
if (pending) {
|
||||
return await pending;
|
||||
}
|
||||
|
||||
// Make request
|
||||
const promise = fetch(url).then(r => r.json());
|
||||
deduplicator.registerPendingRequest('api', 'GET', { url }, promise);
|
||||
|
||||
return await promise;
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### API Response Caching
|
||||
|
||||
```javascript
|
||||
const cache = CacheManager.create({
|
||||
defaultStrategy: CacheStrategy.STALE_WHILE_REVALIDATE,
|
||||
defaultTTL: 300000 // 5 minutes
|
||||
});
|
||||
|
||||
async function getUsers() {
|
||||
return await cache.getOrSet('api:users', async () => {
|
||||
const response = await fetch('/api/users');
|
||||
return await response.json();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Computed Value Caching
|
||||
|
||||
```javascript
|
||||
const cache = CacheManager.create();
|
||||
|
||||
function expensiveComputation(input) {
|
||||
// Expensive operation
|
||||
return input * 2;
|
||||
}
|
||||
|
||||
async function getComputedValue(input) {
|
||||
const key = `computed:${input}`;
|
||||
return await cache.getOrSet(key, () => expensiveComputation(input), {
|
||||
ttl: 3600000 // Cache for 1 hour
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Cache Invalidation
|
||||
|
||||
```javascript
|
||||
// Invalidate all user-related cache when user updates
|
||||
async function updateUser(userId, data) {
|
||||
await fetch(`/api/users/${userId}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
// Invalidate related cache
|
||||
await cache.invalidate(`user:${userId}`);
|
||||
await cache.invalidate('users:list');
|
||||
}
|
||||
```
|
||||
|
||||
### Cache Warming
|
||||
|
||||
```javascript
|
||||
// Warm cache on page load
|
||||
async function warmCache() {
|
||||
await cache.warm(['user:1', 'user:2', 'user:3'], async (key) => {
|
||||
const userId = key.split(':')[1];
|
||||
const response = await fetch(`/api/users/${userId}`);
|
||||
return await response.json();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Choose Appropriate Strategy** - Use cache-first for static data, network-first for dynamic data
|
||||
2. **Set Appropriate TTL** - Balance freshness with performance
|
||||
3. **Invalidate on Updates** - Clear cache when data changes
|
||||
4. **Use Cache Warming** - Preload frequently accessed data
|
||||
5. **Monitor Analytics** - Track cache performance and adjust strategy
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- IndexedDB (for persistent cache)
|
||||
- Promise support
|
||||
|
||||
---
|
||||
|
||||
**Next**: Continue with Phase 3 modules →
|
||||
|
||||
413
docs/modules/error-tracking.md
Normal file
413
docs/modules/error-tracking.md
Normal file
@@ -0,0 +1,413 @@
|
||||
# Error Tracking Module
|
||||
|
||||
**Centralized Error Tracking and Reporting**
|
||||
|
||||
The Error Tracking Module provides comprehensive error tracking, grouping, and reporting capabilities for production applications.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Error Collection** - Automatically capture unhandled errors and promise rejections
|
||||
- **Error Grouping** - Group similar errors to reduce noise
|
||||
- **Error Reporting** - Send errors to backend for analysis
|
||||
- **Error Analytics** - Track error frequency and patterns
|
||||
- **Integration with ErrorBoundary** - Works with LiveComponent ErrorBoundary
|
||||
- **Source Map Support** - Map minified errors to source code
|
||||
- **Error Filtering** - Filter out known or irrelevant errors
|
||||
- **Sampling** - Control error reporting volume with sampling
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { ErrorTracker } from './modules/error-tracking/index.js';
|
||||
|
||||
// Create error tracker
|
||||
const tracker = ErrorTracker.create({
|
||||
endpoint: '/api/errors',
|
||||
enabled: true,
|
||||
sampleRate: 1.0 // Report 100% of errors
|
||||
});
|
||||
|
||||
// Manually capture an error
|
||||
try {
|
||||
// Some code that might throw
|
||||
} catch (error) {
|
||||
tracker.captureException(error, {
|
||||
type: 'user-action',
|
||||
action: 'submit-form'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Module System Integration
|
||||
|
||||
```html
|
||||
<!-- Enable global error tracking -->
|
||||
<script type="module">
|
||||
import { init } from './modules/error-tracking/index.js';
|
||||
|
||||
init({
|
||||
endpoint: '/api/errors',
|
||||
enabled: true,
|
||||
sampleRate: 0.1 // Report 10% of errors
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Access globally -->
|
||||
<script>
|
||||
// Errors are automatically captured
|
||||
// Or manually capture:
|
||||
window.ErrorTracker.captureException(new Error('Something went wrong'), {
|
||||
context: { userId: 123 }
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### ErrorTracker.create(config)
|
||||
|
||||
Create a new ErrorTracker instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.endpoint` - Backend endpoint for error reporting (default: '/api/errors')
|
||||
- `config.enabled` - Enable error tracking (default: true)
|
||||
- `config.sampleRate` - Sampling rate 0.0 to 1.0 (default: 1.0)
|
||||
- `config.maxErrors` - Maximum errors to keep in memory (default: 100)
|
||||
- `config.groupingWindow` - Time window for error grouping in ms (default: 60000)
|
||||
- `config.includeStack` - Include stack traces (default: true)
|
||||
- `config.includeContext` - Include context information (default: true)
|
||||
- `config.includeUserAgent` - Include user agent (default: true)
|
||||
- `config.includeUrl` - Include current URL (default: true)
|
||||
- `config.filters` - Array of filter functions or regex patterns
|
||||
- `config.beforeSend` - Hook to modify errors before sending
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const tracker = ErrorTracker.create({
|
||||
endpoint: '/api/errors',
|
||||
enabled: true,
|
||||
sampleRate: 0.5, // Report 50% of errors
|
||||
filters: [
|
||||
// Filter out specific errors
|
||||
/Script error/i,
|
||||
(error, context) => {
|
||||
// Custom filter logic
|
||||
return error.message.includes('ResizeObserver');
|
||||
}
|
||||
],
|
||||
beforeSend: (errorData) => {
|
||||
// Add additional context
|
||||
errorData.userId = getCurrentUserId();
|
||||
errorData.sessionId = getSessionId();
|
||||
return errorData;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### tracker.captureException(error, context)
|
||||
|
||||
Manually capture an exception.
|
||||
|
||||
**Parameters**:
|
||||
- `error` - Error object or any value
|
||||
- `context` - Additional context information
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
try {
|
||||
// Some code
|
||||
} catch (error) {
|
||||
tracker.captureException(error, {
|
||||
type: 'api-call',
|
||||
endpoint: '/api/users',
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### tracker.report()
|
||||
|
||||
Manually flush error reports to backend.
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
// Report errors immediately
|
||||
await tracker.report();
|
||||
```
|
||||
|
||||
### tracker.getErrorGroups()
|
||||
|
||||
Get grouped errors.
|
||||
|
||||
**Returns**: `Array<ErrorGroup>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const groups = tracker.getErrorGroups();
|
||||
groups.forEach(group => {
|
||||
console.log(`${group.fingerprint}: ${group.count} occurrences`);
|
||||
});
|
||||
```
|
||||
|
||||
### tracker.getErrors()
|
||||
|
||||
Get all captured errors.
|
||||
|
||||
**Returns**: `Array<ErrorData>`
|
||||
|
||||
### tracker.clearErrors()
|
||||
|
||||
Clear all captured errors.
|
||||
|
||||
---
|
||||
|
||||
## Integration with ErrorBoundary
|
||||
|
||||
```javascript
|
||||
import { ErrorTracker } from './modules/error-tracking/index.js';
|
||||
import { ErrorBoundary } from './modules/livecomponent/ErrorBoundary.js';
|
||||
|
||||
const tracker = ErrorTracker.create({
|
||||
endpoint: '/api/errors'
|
||||
});
|
||||
|
||||
// ErrorBoundary automatically captures errors
|
||||
const errorBoundary = new ErrorBoundary(liveComponentManager);
|
||||
|
||||
// Listen for errors
|
||||
window.addEventListener('error-tracker:error', (event) => {
|
||||
const errorData = event.detail;
|
||||
console.error('Error captured:', errorData);
|
||||
|
||||
// Show user-friendly error message
|
||||
showErrorNotification(errorData.message);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Filtering
|
||||
|
||||
### Filter by Regex
|
||||
|
||||
```javascript
|
||||
const tracker = ErrorTracker.create({
|
||||
filters: [
|
||||
/Script error/i, // Filter out script errors
|
||||
/ResizeObserver/i // Filter out ResizeObserver errors
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### Filter by Function
|
||||
|
||||
```javascript
|
||||
const tracker = ErrorTracker.create({
|
||||
filters: [
|
||||
(error, context) => {
|
||||
// Filter out errors from specific domains
|
||||
if (context.url && context.url.includes('localhost')) {
|
||||
return false; // Don't track localhost errors
|
||||
}
|
||||
return true; // Track other errors
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Sampling
|
||||
|
||||
Control error reporting volume with sampling:
|
||||
|
||||
```javascript
|
||||
const tracker = ErrorTracker.create({
|
||||
sampleRate: 0.1 // Report only 10% of errors
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## beforeSend Hook
|
||||
|
||||
Modify errors before sending to backend:
|
||||
|
||||
```javascript
|
||||
const tracker = ErrorTracker.create({
|
||||
beforeSend: (errorData) => {
|
||||
// Add user information
|
||||
errorData.user = {
|
||||
id: getCurrentUserId(),
|
||||
email: getCurrentUserEmail()
|
||||
};
|
||||
|
||||
// Add session information
|
||||
errorData.session = {
|
||||
id: getSessionId(),
|
||||
startTime: getSessionStartTime()
|
||||
};
|
||||
|
||||
// Remove sensitive data
|
||||
delete errorData.context.password;
|
||||
|
||||
// Return modified error data
|
||||
return errorData;
|
||||
|
||||
// Or return null/false to prevent sending
|
||||
// return null;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Grouping
|
||||
|
||||
Errors are automatically grouped by:
|
||||
- Error name
|
||||
- Error message
|
||||
- Stack trace (first 3 lines)
|
||||
|
||||
Similar errors are grouped together to reduce noise:
|
||||
|
||||
```javascript
|
||||
const groups = tracker.getErrorGroups();
|
||||
groups.forEach(group => {
|
||||
console.log(`Error: ${group.fingerprint}`);
|
||||
console.log(`Count: ${group.count}`);
|
||||
console.log(`First seen: ${new Date(group.firstSeen)}`);
|
||||
console.log(`Last seen: ${new Date(group.lastSeen)}`);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backend Integration
|
||||
|
||||
The error tracker sends errors to the backend endpoint:
|
||||
|
||||
```javascript
|
||||
POST /api/errors
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"message": "Cannot read property 'x' of undefined",
|
||||
"name": "TypeError",
|
||||
"stack": "...",
|
||||
"timestamp": 1234567890,
|
||||
"type": "unhandled",
|
||||
"context": {
|
||||
"url": "https://example.com/page",
|
||||
"userAgent": "...",
|
||||
"viewport": { "width": 1920, "height": 1080 }
|
||||
}
|
||||
}
|
||||
],
|
||||
"errorGroups": [
|
||||
{
|
||||
"fingerprint": "TypeError:Cannot read property...",
|
||||
"count": 5,
|
||||
"firstSeen": 1234567890,
|
||||
"lastSeen": 1234567900
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Production Error Tracking
|
||||
|
||||
```javascript
|
||||
const tracker = ErrorTracker.create({
|
||||
endpoint: '/api/errors',
|
||||
enabled: true,
|
||||
sampleRate: 0.1, // Sample 10% in production
|
||||
filters: [
|
||||
// Filter out known issues
|
||||
/ResizeObserver/i,
|
||||
/Script error/i
|
||||
],
|
||||
beforeSend: (errorData) => {
|
||||
// Add user context
|
||||
errorData.userId = getCurrentUserId();
|
||||
return errorData;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Development Error Tracking
|
||||
|
||||
```javascript
|
||||
const tracker = ErrorTracker.create({
|
||||
endpoint: '/api/errors',
|
||||
enabled: true,
|
||||
sampleRate: 1.0, // Track all errors in development
|
||||
includeStack: true,
|
||||
includeContext: true
|
||||
});
|
||||
```
|
||||
|
||||
### API Error Tracking
|
||||
|
||||
```javascript
|
||||
async function apiCall(url, options) {
|
||||
try {
|
||||
const response = await fetch(url, options);
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error: ${response.status}`);
|
||||
}
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
tracker.captureException(error, {
|
||||
type: 'api-error',
|
||||
url,
|
||||
method: options.method || 'GET',
|
||||
status: error.status
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Sampling in Production** - Set sampleRate to 0.1 or lower to reduce backend load
|
||||
2. **Filter Known Issues** - Filter out errors you can't fix (e.g., browser extensions)
|
||||
3. **Add Context** - Use beforeSend to add user, session, or request context
|
||||
4. **Group Errors** - Let the tracker group similar errors automatically
|
||||
5. **Monitor Error Groups** - Track error frequency and patterns
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- Fetch API
|
||||
- Promise support
|
||||
|
||||
---
|
||||
|
||||
**Next**: [Event Bus Module](event-bus.md) →
|
||||
|
||||
370
docs/modules/event-bus.md
Normal file
370
docs/modules/event-bus.md
Normal file
@@ -0,0 +1,370 @@
|
||||
# Event Bus Module
|
||||
|
||||
**Centralized Event System for Cross-Module Communication**
|
||||
|
||||
The Event Bus Module provides a pub/sub event system for decoupled communication between modules, components, and services.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Pub/Sub Pattern** - Publish and subscribe to events
|
||||
- **Namespaced Events** - Organize events with namespaces (e.g., 'user:created')
|
||||
- **Event Filtering** - Filter events by data or conditions
|
||||
- **Event History** - Track event history for debugging
|
||||
- **Integration with LiveComponents** - Works seamlessly with LiveComponents
|
||||
- **Integration with SSE** - Integrate with Server-Sent Events
|
||||
- **Wildcard Support** - Subscribe to event patterns
|
||||
- **Middleware Support** - Transform or filter events with middleware
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { EventBus } from './modules/event-bus/index.js';
|
||||
|
||||
// Create event bus
|
||||
const bus = EventBus.create();
|
||||
|
||||
// Subscribe to an event
|
||||
const unsubscribe = bus.on('user:created', (user) => {
|
||||
console.log('User created:', user);
|
||||
});
|
||||
|
||||
// Emit an event
|
||||
bus.emit('user:created', { id: 1, name: 'John' });
|
||||
|
||||
// Unsubscribe
|
||||
unsubscribe();
|
||||
```
|
||||
|
||||
### Module System Integration
|
||||
|
||||
```html
|
||||
<!-- Enable global event bus -->
|
||||
<script type="module">
|
||||
import { init } from './modules/event-bus/index.js';
|
||||
|
||||
init({
|
||||
enableHistory: true,
|
||||
maxHistorySize: 100
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Access globally -->
|
||||
<script>
|
||||
// Subscribe
|
||||
window.EventBus.on('user:created', (user) => {
|
||||
console.log('User created:', user);
|
||||
});
|
||||
|
||||
// Emit
|
||||
window.EventBus.emit('user:created', { id: 1, name: 'John' });
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### EventBus.create(config)
|
||||
|
||||
Create a new EventBus instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.enableHistory` - Enable event history (default: false)
|
||||
- `config.maxHistorySize` - Maximum history size (default: 100)
|
||||
- `config.enableWildcards` - Enable wildcard patterns (default: true)
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const bus = EventBus.create({
|
||||
enableHistory: true,
|
||||
maxHistorySize: 50
|
||||
});
|
||||
```
|
||||
|
||||
### bus.on(eventName, callback, options)
|
||||
|
||||
Subscribe to an event.
|
||||
|
||||
**Parameters**:
|
||||
- `eventName` - Event name (supports wildcards: 'user:*', '*')
|
||||
- `callback` - Callback function: `(data, eventName, options) => void`
|
||||
- `options.once` - Subscribe only once (default: false)
|
||||
- `options.priority` - Subscription priority (default: 0)
|
||||
- `options.filter` - Filter function: `(data, options) => boolean`
|
||||
|
||||
**Returns**: Unsubscribe function
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
// Basic subscription
|
||||
const unsubscribe = bus.on('user:created', (user) => {
|
||||
console.log('User created:', user);
|
||||
});
|
||||
|
||||
// Once subscription
|
||||
bus.once('user:created', (user) => {
|
||||
console.log('User created (once):', user);
|
||||
});
|
||||
|
||||
// Priority subscription
|
||||
bus.on('user:created', (user) => {
|
||||
console.log('High priority handler');
|
||||
}, { priority: 10 });
|
||||
|
||||
// Filtered subscription
|
||||
bus.on('user:created', (user) => {
|
||||
console.log('Admin user created');
|
||||
}, {
|
||||
filter: (user) => user.role === 'admin'
|
||||
});
|
||||
```
|
||||
|
||||
### bus.once(eventName, callback, options)
|
||||
|
||||
Subscribe to an event once (auto-unsubscribe after first emission).
|
||||
|
||||
**Parameters**: Same as `on()`
|
||||
|
||||
**Returns**: Unsubscribe function
|
||||
|
||||
### bus.off(eventName, callback)
|
||||
|
||||
Unsubscribe from an event.
|
||||
|
||||
**Parameters**:
|
||||
- `eventName` - Event name
|
||||
- `callback` - Callback function to remove
|
||||
|
||||
### bus.emit(eventName, data, options)
|
||||
|
||||
Emit an event.
|
||||
|
||||
**Parameters**:
|
||||
- `eventName` - Event name
|
||||
- `data` - Event data
|
||||
- `options` - Event options
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
bus.emit('user:created', { id: 1, name: 'John' });
|
||||
bus.emit('user:updated', { id: 1, name: 'Jane' }, { source: 'api' });
|
||||
```
|
||||
|
||||
### bus.use(middleware)
|
||||
|
||||
Add middleware to transform or filter events.
|
||||
|
||||
**Parameters**:
|
||||
- `middleware` - Middleware function: `(eventName, data, options) => data | null | false`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
// Logging middleware
|
||||
bus.use((eventName, data, options) => {
|
||||
console.log(`Event: ${eventName}`, data);
|
||||
return data; // Continue
|
||||
});
|
||||
|
||||
// Filtering middleware
|
||||
bus.use((eventName, data, options) => {
|
||||
if (eventName.startsWith('debug:')) {
|
||||
return null; // Block debug events in production
|
||||
}
|
||||
return data;
|
||||
});
|
||||
```
|
||||
|
||||
### bus.getHistory(filter)
|
||||
|
||||
Get event history.
|
||||
|
||||
**Parameters**:
|
||||
- `filter` - Optional filter (string for event name, or function)
|
||||
|
||||
**Returns**: `Array<EventHistoryItem>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
// Get all history
|
||||
const history = bus.getHistory();
|
||||
|
||||
// Get history for specific event
|
||||
const userHistory = bus.getHistory('user:created');
|
||||
|
||||
// Get history with custom filter
|
||||
const recentHistory = bus.getHistory((item) => {
|
||||
return Date.now() - item.timestamp < 60000; // Last minute
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Namespaced Events
|
||||
|
||||
Organize events with namespaces:
|
||||
|
||||
```javascript
|
||||
// Subscribe to namespace
|
||||
bus.on('user:*', (data, eventName) => {
|
||||
console.log(`User event: ${eventName}`, data);
|
||||
});
|
||||
|
||||
// Emit namespaced events
|
||||
bus.emit('user:created', { id: 1 });
|
||||
bus.emit('user:updated', { id: 1 });
|
||||
bus.emit('user:deleted', { id: 1 });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Wildcard Support
|
||||
|
||||
Subscribe to event patterns:
|
||||
|
||||
```javascript
|
||||
// Subscribe to all events
|
||||
bus.on('*', (data, eventName) => {
|
||||
console.log(`Event: ${eventName}`, data);
|
||||
});
|
||||
|
||||
// Subscribe to pattern
|
||||
bus.on('user:*', (data, eventName) => {
|
||||
console.log(`User event: ${eventName}`, data);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with LiveComponents
|
||||
|
||||
```javascript
|
||||
import { EventBus } from './modules/event-bus/index.js';
|
||||
import { LiveComponentManager } from './modules/livecomponent/index.js';
|
||||
|
||||
const bus = EventBus.create();
|
||||
const lcManager = LiveComponentManager.getInstance();
|
||||
|
||||
// Listen for LiveComponent events
|
||||
bus.on('livecomponent:action-executed', (data) => {
|
||||
console.log('Action executed:', data);
|
||||
});
|
||||
|
||||
// Emit LiveComponent events
|
||||
lcManager.on('action-executed', (componentId, actionName, params) => {
|
||||
bus.emit('livecomponent:action-executed', {
|
||||
componentId,
|
||||
actionName,
|
||||
params
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with SSE
|
||||
|
||||
```javascript
|
||||
import { EventBus } from './modules/event-bus/index.js';
|
||||
import { SseClient } from './modules/sse/index.js';
|
||||
|
||||
const bus = EventBus.create();
|
||||
const sse = new SseClient(['updates']);
|
||||
|
||||
// Forward SSE messages to event bus
|
||||
sse.on('message', (data) => {
|
||||
bus.emit('sse:message', data);
|
||||
});
|
||||
|
||||
// Listen for SSE events
|
||||
bus.on('sse:message', (data) => {
|
||||
console.log('SSE message:', data);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Module Communication
|
||||
|
||||
```javascript
|
||||
// Module A
|
||||
const bus = EventBus.create();
|
||||
bus.emit('module-a:data-ready', { data: [...] });
|
||||
|
||||
// Module B
|
||||
bus.on('module-a:data-ready', (data) => {
|
||||
processData(data);
|
||||
});
|
||||
```
|
||||
|
||||
### Component Communication
|
||||
|
||||
```javascript
|
||||
// Component A
|
||||
bus.emit('component:user-selected', { userId: 123 });
|
||||
|
||||
// Component B
|
||||
bus.on('component:user-selected', (data) => {
|
||||
loadUserDetails(data.userId);
|
||||
});
|
||||
```
|
||||
|
||||
### Global Notifications
|
||||
|
||||
```javascript
|
||||
// Emit notification
|
||||
bus.emit('notification:show', {
|
||||
type: 'success',
|
||||
message: 'Operation completed'
|
||||
});
|
||||
|
||||
// Listen for notifications
|
||||
bus.on('notification:show', (data) => {
|
||||
showNotification(data.type, data.message);
|
||||
});
|
||||
```
|
||||
|
||||
### Analytics Events
|
||||
|
||||
```javascript
|
||||
// Track events
|
||||
bus.on('*', (data, eventName) => {
|
||||
// Send to analytics
|
||||
analytics.track(eventName, data);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Namespaces** - Organize events with namespaces (e.g., 'user:created')
|
||||
2. **Document Events** - Document all events and their data structures
|
||||
3. **Use TypeScript** - Use TypeScript definitions for type safety
|
||||
4. **Clean Up Subscriptions** - Always unsubscribe when done
|
||||
5. **Use Middleware** - Use middleware for cross-cutting concerns (logging, analytics)
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- Map and Set support
|
||||
|
||||
---
|
||||
|
||||
**Next**: Continue with Phase 2 modules →
|
||||
|
||||
364
docs/modules/router.md
Normal file
364
docs/modules/router.md
Normal file
@@ -0,0 +1,364 @@
|
||||
# Router Enhancement Module
|
||||
|
||||
**Enhanced Routing with Guards, Middleware, and Analytics**
|
||||
|
||||
The Router Enhancement Module provides advanced routing capabilities with access control, middleware, lazy loading, and analytics.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Route Guards** - Access control for routes (auth, permissions, roles)
|
||||
- **Route Middleware** - Cross-cutting concerns (analytics, loading, etc.)
|
||||
- **Lazy Route Loading** - Load routes on demand
|
||||
- **Route Analytics** - Track navigation patterns
|
||||
- **Integration with LiveComponents** - Seamless integration with LiveComponent system
|
||||
- **History & Hash Modes** - Support for both history and hash routing
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { Router, BuiltInGuards, BuiltInMiddleware } from './modules/router/index.js';
|
||||
|
||||
// Create router
|
||||
const router = Router.create({
|
||||
mode: 'history',
|
||||
base: '/',
|
||||
enableAnalytics: true
|
||||
});
|
||||
|
||||
// Register routes
|
||||
router.routes([
|
||||
{
|
||||
path: '/',
|
||||
component: () => '<div>Home</div>',
|
||||
name: 'home',
|
||||
title: 'Home'
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: () => '<div>Dashboard</div>',
|
||||
name: 'dashboard',
|
||||
title: 'Dashboard',
|
||||
guards: ['auth'],
|
||||
middleware: ['analytics', 'loading']
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
component: () => import('./components/Admin.js'),
|
||||
name: 'admin',
|
||||
title: 'Admin',
|
||||
guards: ['auth', 'role:admin'],
|
||||
lazy: true
|
||||
}
|
||||
]);
|
||||
|
||||
// Register guards
|
||||
router.guard('auth', async (to, from) => {
|
||||
const isAuthenticated = await checkAuth();
|
||||
if (!isAuthenticated) {
|
||||
return '/login'; // Redirect to login
|
||||
}
|
||||
return true; // Allow navigation
|
||||
});
|
||||
|
||||
// Add global middleware
|
||||
router.use(BuiltInMiddleware.analytics);
|
||||
router.use(BuiltInMiddleware.scrollToTop);
|
||||
|
||||
// Navigate
|
||||
await router.navigate('/dashboard');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### Router.create(config)
|
||||
|
||||
Create a new Router instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.mode` - Routing mode: 'history' or 'hash'
|
||||
- `config.base` - Base path
|
||||
- `config.enableAnalytics` - Enable route analytics
|
||||
|
||||
### router.route(path, config)
|
||||
|
||||
Register a single route.
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Route path
|
||||
- `config.component` - Component (string, function, or HTMLElement)
|
||||
- `config.name` - Route name
|
||||
- `config.title` - Page title
|
||||
- `config.guards` - Array of guard names
|
||||
- `config.middleware` - Array of middleware
|
||||
- `config.lazy` - Lazy load component
|
||||
|
||||
### router.guard(name, guardFn)
|
||||
|
||||
Register a route guard.
|
||||
|
||||
**Parameters**:
|
||||
- `name` - Guard name
|
||||
- `guardFn` - Guard function: `(to, from, context) => boolean | string`
|
||||
|
||||
### router.use(middleware)
|
||||
|
||||
Add global middleware.
|
||||
|
||||
**Parameters**:
|
||||
- `middleware` - Middleware instance, function, or built-in name
|
||||
|
||||
### router.beforeEach(hook)
|
||||
|
||||
Add before navigation hook.
|
||||
|
||||
**Parameters**:
|
||||
- `hook` - Hook function: `(to, from) => boolean | string | void`
|
||||
|
||||
### router.afterEach(hook)
|
||||
|
||||
Add after navigation hook.
|
||||
|
||||
**Parameters**:
|
||||
- `hook` - Hook function: `(to, from) => void`
|
||||
|
||||
### router.navigate(path, options)
|
||||
|
||||
Navigate to a route.
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Route path
|
||||
- `options.container` - Container element
|
||||
- `options.updateHistory` - Update browser history
|
||||
|
||||
**Returns**: `Promise<boolean>`
|
||||
|
||||
---
|
||||
|
||||
## Route Guards
|
||||
|
||||
### Authentication Guard
|
||||
|
||||
```javascript
|
||||
router.guard('auth', async (to, from) => {
|
||||
if (!isAuthenticated()) {
|
||||
return '/login';
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Use in route
|
||||
router.route('/dashboard', {
|
||||
component: DashboardComponent,
|
||||
guards: ['auth']
|
||||
});
|
||||
```
|
||||
|
||||
### Role Guard
|
||||
|
||||
```javascript
|
||||
router.guard('admin', async (to, from) => {
|
||||
if (getUserRole() !== 'admin') {
|
||||
return '/unauthorized';
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Use in route
|
||||
router.route('/admin', {
|
||||
component: AdminComponent,
|
||||
guards: ['auth', 'admin']
|
||||
});
|
||||
```
|
||||
|
||||
### Built-in Guards
|
||||
|
||||
```javascript
|
||||
// Use built-in guards
|
||||
router.route('/dashboard', {
|
||||
component: DashboardComponent,
|
||||
guards: ['auth'] // Uses BuiltInGuards.auth
|
||||
});
|
||||
|
||||
router.route('/login', {
|
||||
component: LoginComponent,
|
||||
guards: ['guest'] // Uses BuiltInGuards.guest
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Route Middleware
|
||||
|
||||
### Analytics Middleware
|
||||
|
||||
```javascript
|
||||
// Use built-in analytics middleware
|
||||
router.use('analytics');
|
||||
|
||||
// Or custom middleware
|
||||
router.use(RouteMiddleware.create('custom', async (to, from, next) => {
|
||||
// Track navigation
|
||||
analytics.track('page_view', { path: to.path });
|
||||
next();
|
||||
}));
|
||||
```
|
||||
|
||||
### Loading Middleware
|
||||
|
||||
```javascript
|
||||
router.use('loading'); // Shows loading indicator during navigation
|
||||
```
|
||||
|
||||
### Scroll to Top Middleware
|
||||
|
||||
```javascript
|
||||
router.use('scroll-to-top'); // Scrolls to top after navigation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lazy Route Loading
|
||||
|
||||
```javascript
|
||||
router.route('/admin', {
|
||||
component: () => import('./components/Admin.js'),
|
||||
lazy: true
|
||||
});
|
||||
|
||||
// Or with dynamic import
|
||||
router.route('/user/:id', {
|
||||
component: async (route) => {
|
||||
const UserComponent = await import('./components/User.js');
|
||||
return UserComponent.default(route.params.id);
|
||||
},
|
||||
lazy: true
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with LiveComponents
|
||||
|
||||
```javascript
|
||||
import { Router } from './modules/router/index.js';
|
||||
import { LiveComponent } from './modules/livecomponent/index.js';
|
||||
|
||||
const router = Router.create();
|
||||
|
||||
router.route('/dashboard', {
|
||||
component: async (route) => {
|
||||
// Initialize LiveComponents after navigation
|
||||
const container = document.querySelector('main');
|
||||
container.innerHTML = '<div data-live-component="dashboard"></div>';
|
||||
|
||||
// Initialize LiveComponent
|
||||
const component = container.querySelector('[data-live-component]');
|
||||
LiveComponent.init(component);
|
||||
|
||||
return container;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Route Analytics
|
||||
|
||||
```javascript
|
||||
const router = Router.create({ enableAnalytics: true });
|
||||
|
||||
// Get analytics
|
||||
const analytics = router.getAnalytics();
|
||||
console.log('Total navigations:', analytics.totalNavigations);
|
||||
console.log('Navigation history:', analytics.navigations);
|
||||
|
||||
// Listen for navigation events
|
||||
window.addEventListener('router:navigation', (event) => {
|
||||
const { to, from, timestamp } = event.detail;
|
||||
console.log(`Navigated from ${from} to ${to}`);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Protected Routes
|
||||
|
||||
```javascript
|
||||
router.routes([
|
||||
{
|
||||
path: '/',
|
||||
component: HomeComponent,
|
||||
name: 'home'
|
||||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: DashboardComponent,
|
||||
name: 'dashboard',
|
||||
guards: ['auth']
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
component: AdminComponent,
|
||||
name: 'admin',
|
||||
guards: ['auth', 'admin']
|
||||
}
|
||||
]);
|
||||
```
|
||||
|
||||
### Route with Middleware
|
||||
|
||||
```javascript
|
||||
router.route('/api-data', {
|
||||
component: ApiDataComponent,
|
||||
middleware: ['analytics', 'loading']
|
||||
});
|
||||
```
|
||||
|
||||
### Lazy Loading
|
||||
|
||||
```javascript
|
||||
router.route('/heavy-page', {
|
||||
component: () => import('./pages/HeavyPage.js'),
|
||||
lazy: true,
|
||||
guards: ['auth']
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Guards for Access Control** - Protect routes with guards
|
||||
2. **Use Middleware for Cross-Cutting Concerns** - Analytics, loading, etc.
|
||||
3. **Lazy Load Heavy Routes** - Improve initial load time
|
||||
4. **Track Navigation** - Use analytics to understand user behavior
|
||||
5. **Handle Errors** - Provide fallback routes for errors
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- History API (for history mode)
|
||||
- Promise support
|
||||
|
||||
---
|
||||
|
||||
**Next**: [Analytics Module](analytics.md) →
|
||||
|
||||
382
docs/modules/security.md
Normal file
382
docs/modules/security.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# Security Module
|
||||
|
||||
**Security Utilities: CSRF, XSS Protection, and CSP Helpers**
|
||||
|
||||
The Security Module provides comprehensive security utilities for client-side security management.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **CSRF Token Management** - Automatic token refresh and management
|
||||
- **XSS Protection** - HTML sanitization and input validation
|
||||
- **Content Security Policy** - CSP validation and helpers
|
||||
- **Security Headers Validation** - Client-side security header checks
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { SecurityManager } from './modules/security/index.js';
|
||||
|
||||
// Create security manager
|
||||
const security = SecurityManager.create({
|
||||
csrf: {
|
||||
autoRefresh: true,
|
||||
refreshInterval: 30 * 60 * 1000 // 30 minutes
|
||||
},
|
||||
xss: {
|
||||
enabled: true,
|
||||
sanitizeOnInput: false
|
||||
}
|
||||
});
|
||||
|
||||
// Get CSRF token
|
||||
const token = security.getCsrfToken();
|
||||
|
||||
// Use in fetch request
|
||||
const response = await fetch('/api/endpoint', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...security.getCsrfTokenHeader(),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
```
|
||||
|
||||
### Module System Integration
|
||||
|
||||
```html
|
||||
<!-- Enable global security manager -->
|
||||
<script type="module">
|
||||
import { init } from './modules/security/index.js';
|
||||
|
||||
init({
|
||||
csrf: {
|
||||
autoRefresh: true
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Access globally -->
|
||||
<script>
|
||||
// Get CSRF token
|
||||
const token = window.SecurityManager.getCsrfToken();
|
||||
|
||||
// Refresh token
|
||||
await window.SecurityManager.refreshCsrfToken();
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### SecurityManager.create(config)
|
||||
|
||||
Create a new SecurityManager instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.csrf` - CSRF manager configuration
|
||||
- `config.xss` - XSS protection configuration
|
||||
- `config.csp` - CSP configuration
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const security = SecurityManager.create({
|
||||
csrf: {
|
||||
tokenName: '_token',
|
||||
headerName: 'X-CSRF-TOKEN',
|
||||
autoRefresh: true,
|
||||
refreshInterval: 30 * 60 * 1000
|
||||
},
|
||||
xss: {
|
||||
enabled: true,
|
||||
sanitizeOnInput: false
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### security.getCsrfToken()
|
||||
|
||||
Get current CSRF token.
|
||||
|
||||
**Returns**: `string | null`
|
||||
|
||||
### security.getCsrfTokenHeader()
|
||||
|
||||
Get CSRF token header object for use in fetch requests.
|
||||
|
||||
**Returns**: `Record<string, string>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const headers = {
|
||||
...security.getCsrfTokenHeader(),
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
```
|
||||
|
||||
### security.refreshCsrfToken()
|
||||
|
||||
Manually refresh CSRF token.
|
||||
|
||||
**Returns**: `Promise<void>`
|
||||
|
||||
### security.escapeHtml(text)
|
||||
|
||||
Escape HTML to prevent XSS.
|
||||
|
||||
**Parameters**:
|
||||
- `text` - Text to escape
|
||||
|
||||
**Returns**: `string`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const safe = security.escapeHtml('<script>alert("xss")</script>');
|
||||
// Returns: <script>alert("xss")</script>
|
||||
```
|
||||
|
||||
### security.validateUrl(url)
|
||||
|
||||
Validate URL to prevent XSS.
|
||||
|
||||
**Parameters**:
|
||||
- `url` - URL to validate
|
||||
|
||||
**Returns**: `boolean`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
if (security.validateUrl(userInput)) {
|
||||
// Safe to use
|
||||
} else {
|
||||
// Potentially dangerous
|
||||
}
|
||||
```
|
||||
|
||||
### security.validateSecurityHeaders()
|
||||
|
||||
Validate security headers.
|
||||
|
||||
**Returns**: `SecurityHeadersValidation`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const validation = security.validateSecurityHeaders();
|
||||
if (!validation.valid) {
|
||||
console.warn('Security issues:', validation.issues);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CSRF Token Management
|
||||
|
||||
### Automatic Token Refresh
|
||||
|
||||
```javascript
|
||||
const security = SecurityManager.create({
|
||||
csrf: {
|
||||
autoRefresh: true,
|
||||
refreshInterval: 30 * 60 * 1000 // Refresh every 30 minutes
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Manual Token Refresh
|
||||
|
||||
```javascript
|
||||
await security.refreshCsrfToken();
|
||||
```
|
||||
|
||||
### Using Token in Requests
|
||||
|
||||
```javascript
|
||||
// Get token header
|
||||
const headers = security.getCsrfTokenHeader();
|
||||
|
||||
// Use in fetch
|
||||
const response = await fetch('/api/endpoint', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...headers,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## XSS Protection
|
||||
|
||||
### HTML Sanitization
|
||||
|
||||
```javascript
|
||||
const security = SecurityManager.create({
|
||||
xss: {
|
||||
enabled: true,
|
||||
sanitizeOnInput: true // Auto-sanitize on input
|
||||
}
|
||||
});
|
||||
|
||||
// Manual sanitization
|
||||
const safe = security.sanitizeHtml(userInput);
|
||||
```
|
||||
|
||||
### HTML Escaping
|
||||
|
||||
```javascript
|
||||
const escaped = security.escapeHtml('<script>alert("xss")</script>');
|
||||
```
|
||||
|
||||
### URL Validation
|
||||
|
||||
```javascript
|
||||
if (security.validateUrl(userInput)) {
|
||||
// Safe URL
|
||||
window.location.href = userInput;
|
||||
} else {
|
||||
// Potentially dangerous
|
||||
console.error('Invalid URL');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Content Security Policy
|
||||
|
||||
### CSP Validation
|
||||
|
||||
```javascript
|
||||
const security = SecurityManager.create({
|
||||
csp: {
|
||||
enabled: true
|
||||
}
|
||||
});
|
||||
|
||||
// CSP is automatically validated on init
|
||||
// Check validation results
|
||||
const validation = security.validateSecurityHeaders();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with LiveComponents
|
||||
|
||||
```javascript
|
||||
import { SecurityManager } from './modules/security/index.js';
|
||||
import { LiveComponentManager } from './modules/livecomponent/index.js';
|
||||
|
||||
const security = SecurityManager.create();
|
||||
|
||||
// LiveComponents automatically use CSRF tokens
|
||||
// But you can also manually update tokens
|
||||
security.csrfManager.updateAllTokens();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with Forms
|
||||
|
||||
```javascript
|
||||
import { SecurityManager } from './modules/security/index.js';
|
||||
|
||||
const security = SecurityManager.create();
|
||||
|
||||
// Forms automatically get updated CSRF tokens
|
||||
// Listen for token refresh events
|
||||
window.addEventListener('csrf:token-refreshed', (event) => {
|
||||
console.log('CSRF token refreshed:', event.detail.token);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### API Requests with CSRF
|
||||
|
||||
```javascript
|
||||
const security = SecurityManager.create();
|
||||
|
||||
async function apiCall(url, data) {
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...security.getCsrfTokenHeader(),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
return response.json();
|
||||
}
|
||||
```
|
||||
|
||||
### User Input Sanitization
|
||||
|
||||
```javascript
|
||||
const security = SecurityManager.create();
|
||||
|
||||
function handleUserInput(input) {
|
||||
// Sanitize HTML
|
||||
const sanitized = security.sanitizeHtml(input);
|
||||
|
||||
// Escape for display
|
||||
const escaped = security.escapeHtml(input);
|
||||
|
||||
// Validate URL
|
||||
if (security.validateUrl(input)) {
|
||||
// Safe to use as URL
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Security Headers Check
|
||||
|
||||
```javascript
|
||||
const security = SecurityManager.create();
|
||||
|
||||
// Validate security headers
|
||||
const validation = security.validateSecurityHeaders();
|
||||
if (!validation.valid) {
|
||||
console.warn('Security issues detected:', validation.issues);
|
||||
// Report to backend or show warning
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always Use CSRF Tokens** - Include CSRF tokens in all state-changing requests
|
||||
2. **Sanitize User Input** - Sanitize HTML before displaying user content
|
||||
3. **Validate URLs** - Always validate URLs before using them
|
||||
4. **Enable Auto-Refresh** - Keep CSRF tokens fresh with auto-refresh
|
||||
5. **Check Security Headers** - Validate security headers in development
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- Fetch API
|
||||
- CustomEvent support
|
||||
|
||||
---
|
||||
|
||||
**Next**: [Cache Manager Module](cache-manager.md) →
|
||||
|
||||
397
docs/modules/state-manager.md
Normal file
397
docs/modules/state-manager.md
Normal file
@@ -0,0 +1,397 @@
|
||||
# State Manager Module
|
||||
|
||||
**Centralized, Reactive State Management for Client-Side State**
|
||||
|
||||
The State Manager Module provides a centralized state management system similar to Redux or Vuex, with support for state persistence, cross-tab synchronization, and integration with LiveComponents.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Reactive State Store** - Subscribe to state changes and react automatically
|
||||
- **State Persistence** - Automatically save and restore state from localStorage or sessionStorage
|
||||
- **Cross-Tab Synchronization** - Keep state synchronized across browser tabs
|
||||
- **Integration with LiveComponents** - Seamless integration with LiveComponent state
|
||||
- **Time-Travel Debugging** - Debug state changes with history and time-travel
|
||||
- **Middleware Support** - Extend functionality with custom middleware
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { StateManager } from './modules/state-manager/index.js';
|
||||
|
||||
// Create a state manager
|
||||
const store = StateManager.create({
|
||||
initialState: {
|
||||
user: { name: '', email: '' },
|
||||
cart: { items: [], total: 0 },
|
||||
ui: { sidebarOpen: false }
|
||||
}
|
||||
});
|
||||
|
||||
// Set state
|
||||
store.set('user.name', 'John Doe');
|
||||
store.set('cart.items', [{ id: 1, name: 'Product' }]);
|
||||
|
||||
// Get state
|
||||
const userName = store.get('user.name');
|
||||
const cartItems = store.get('cart.items', []);
|
||||
|
||||
// Subscribe to changes
|
||||
const unsubscribe = store.subscribe('cart.items', (items) => {
|
||||
console.log('Cart items changed:', items);
|
||||
updateCartUI(items);
|
||||
});
|
||||
|
||||
// Unsubscribe
|
||||
unsubscribe();
|
||||
```
|
||||
|
||||
### Module System Integration
|
||||
|
||||
```html
|
||||
<!-- Enable global state manager -->
|
||||
<script type="module">
|
||||
import { init } from './modules/state-manager/index.js';
|
||||
|
||||
init({
|
||||
initialState: {
|
||||
user: {},
|
||||
cart: {}
|
||||
},
|
||||
persistence: {
|
||||
enabled: true,
|
||||
storage: 'localStorage',
|
||||
key: 'app-state'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- Access globally -->
|
||||
<script>
|
||||
window.StateManager.set('user.name', 'John');
|
||||
const name = window.StateManager.get('user.name');
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### StateManager.create(config)
|
||||
|
||||
Create a new StateManager instance.
|
||||
|
||||
**Parameters**:
|
||||
- `config.initialState` - Initial state object
|
||||
- `config.maxHistorySize` - Maximum history size (default: 50)
|
||||
- `config.enableHistory` - Enable history for time-travel (default: false)
|
||||
- `config.persistence.enabled` - Enable state persistence (default: false)
|
||||
- `config.persistence.storage` - Storage type: 'localStorage' or 'sessionStorage' (default: 'localStorage')
|
||||
- `config.persistence.key` - Storage key (default: 'app-state')
|
||||
- `config.persistence.paths` - Array of paths to persist (empty = all)
|
||||
- `config.sync.enabled` - Enable cross-tab synchronization (default: false)
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const store = StateManager.create({
|
||||
initialState: { user: {}, cart: {} },
|
||||
persistence: {
|
||||
enabled: true,
|
||||
storage: 'localStorage',
|
||||
paths: ['user', 'cart'] // Only persist these paths
|
||||
},
|
||||
sync: {
|
||||
enabled: true // Sync across tabs
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### store.getState()
|
||||
|
||||
Get the entire state object.
|
||||
|
||||
**Returns**: `Record<string, any>`
|
||||
|
||||
### store.get(path, defaultValue)
|
||||
|
||||
Get state at a specific path.
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Dot-separated path (e.g., 'user.name')
|
||||
- `defaultValue` - Default value if path doesn't exist
|
||||
|
||||
**Returns**: `any`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const userName = store.get('user.name', 'Guest');
|
||||
const cartTotal = store.get('cart.total', 0);
|
||||
```
|
||||
|
||||
### store.set(path, value)
|
||||
|
||||
Set state at a specific path.
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Dot-separated path
|
||||
- `value` - Value to set
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
store.set('user.name', 'John Doe');
|
||||
store.set('cart.items', [{ id: 1, name: 'Product' }]);
|
||||
```
|
||||
|
||||
### store.dispatch(action)
|
||||
|
||||
Dispatch an action (Redux-style).
|
||||
|
||||
**Parameters**:
|
||||
- `action` - Action object with `type` property, or a thunk function
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
// Simple action
|
||||
store.dispatch({
|
||||
type: 'ADD_TO_CART',
|
||||
productId: 123,
|
||||
quantity: 1
|
||||
});
|
||||
|
||||
// Thunk (async action)
|
||||
store.dispatch(async (dispatch, getState) => {
|
||||
const response = await fetch('/api/products');
|
||||
const products = await response.json();
|
||||
dispatch({ type: 'SET_PRODUCTS', products });
|
||||
});
|
||||
```
|
||||
|
||||
### store.subscribe(path, callback)
|
||||
|
||||
Subscribe to state changes at a specific path.
|
||||
|
||||
**Parameters**:
|
||||
- `path` - Dot-separated path, or '*' for all changes
|
||||
- `callback` - Callback function: `(newValue, oldValue, path) => void`
|
||||
|
||||
**Returns**: Unsubscribe function
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const unsubscribe = store.subscribe('cart.items', (items, oldItems, path) => {
|
||||
console.log(`Cart items changed at ${path}:`, items);
|
||||
updateCartUI(items);
|
||||
});
|
||||
|
||||
// Later...
|
||||
unsubscribe();
|
||||
```
|
||||
|
||||
### store.subscribeAll(callback)
|
||||
|
||||
Subscribe to all state changes.
|
||||
|
||||
**Parameters**:
|
||||
- `callback` - Callback function: `(state) => void`
|
||||
|
||||
**Returns**: Unsubscribe function
|
||||
|
||||
### store.use(middleware)
|
||||
|
||||
Add middleware to the state manager.
|
||||
|
||||
**Parameters**:
|
||||
- `middleware` - Middleware function: `(action, getState) => action | null`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
// Logging middleware
|
||||
store.use((action, getState) => {
|
||||
console.log('Action:', action);
|
||||
console.log('Current state:', getState());
|
||||
return action; // Return action to continue, or null to block
|
||||
});
|
||||
|
||||
// Validation middleware
|
||||
store.use((action, getState) => {
|
||||
if (action.type === 'SET' && action.path === 'user.email') {
|
||||
if (!isValidEmail(action.value)) {
|
||||
console.error('Invalid email');
|
||||
return null; // Block the action
|
||||
}
|
||||
}
|
||||
return action;
|
||||
});
|
||||
```
|
||||
|
||||
### store.getHistory()
|
||||
|
||||
Get action history for time-travel debugging.
|
||||
|
||||
**Returns**: `Array<HistoryPoint>`
|
||||
|
||||
### store.timeTravel(index)
|
||||
|
||||
Time-travel to a specific history point.
|
||||
|
||||
**Parameters**:
|
||||
- `index` - History index
|
||||
|
||||
### store.reset()
|
||||
|
||||
Reset state to initial state.
|
||||
|
||||
### store.destroy()
|
||||
|
||||
Destroy the state manager and clean up resources.
|
||||
|
||||
---
|
||||
|
||||
## Integration with LiveComponents
|
||||
|
||||
```javascript
|
||||
import { StateManager } from './modules/state-manager/index.js';
|
||||
import { LiveComponentManager } from './modules/livecomponent/index.js';
|
||||
|
||||
const store = StateManager.create();
|
||||
const lcManager = LiveComponentManager.getInstance();
|
||||
|
||||
// Sync LiveComponent state with StateManager
|
||||
lcManager.on('component:state-updated', (componentId, state) => {
|
||||
store.set(`livecomponents.${componentId}`, state);
|
||||
});
|
||||
|
||||
// Update LiveComponent from StateManager
|
||||
store.subscribe('livecomponents', (state) => {
|
||||
Object.entries(state).forEach(([componentId, componentState]) => {
|
||||
lcManager.updateComponentState(componentId, componentState);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### User Preferences
|
||||
|
||||
```javascript
|
||||
const store = StateManager.create({
|
||||
initialState: {
|
||||
preferences: {
|
||||
theme: 'light',
|
||||
language: 'en',
|
||||
notifications: true
|
||||
}
|
||||
},
|
||||
persistence: {
|
||||
enabled: true,
|
||||
storage: 'localStorage',
|
||||
paths: ['preferences']
|
||||
}
|
||||
});
|
||||
|
||||
// Save preference
|
||||
store.set('preferences.theme', 'dark');
|
||||
|
||||
// Load preference
|
||||
const theme = store.get('preferences.theme', 'light');
|
||||
```
|
||||
|
||||
### Shopping Cart
|
||||
|
||||
```javascript
|
||||
const store = StateManager.create({
|
||||
initialState: {
|
||||
cart: {
|
||||
items: [],
|
||||
total: 0
|
||||
}
|
||||
},
|
||||
persistence: {
|
||||
enabled: true,
|
||||
storage: 'sessionStorage',
|
||||
paths: ['cart']
|
||||
},
|
||||
sync: {
|
||||
enabled: true // Sync cart across tabs
|
||||
}
|
||||
});
|
||||
|
||||
// Add item
|
||||
store.set('cart.items', [
|
||||
...store.get('cart.items', []),
|
||||
{ id: 1, name: 'Product', price: 99.99 }
|
||||
]);
|
||||
|
||||
// Calculate total
|
||||
store.subscribe('cart.items', (items) => {
|
||||
const total = items.reduce((sum, item) => sum + item.price, 0);
|
||||
store.set('cart.total', total);
|
||||
});
|
||||
```
|
||||
|
||||
### UI State
|
||||
|
||||
```javascript
|
||||
const store = StateManager.create({
|
||||
initialState: {
|
||||
ui: {
|
||||
sidebarOpen: false,
|
||||
modalOpen: false,
|
||||
activeTab: 'home'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle sidebar
|
||||
store.set('ui.sidebarOpen', !store.get('ui.sidebarOpen'));
|
||||
|
||||
// Subscribe to UI changes
|
||||
store.subscribe('ui', (uiState) => {
|
||||
updateUI(uiState);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Scoped State Managers** - Create separate state managers for different features
|
||||
2. **Persist Only Necessary Data** - Use `paths` to limit what gets persisted
|
||||
3. **Use Middleware for Cross-Cutting Concerns** - Logging, validation, etc.
|
||||
4. **Subscribe Selectively** - Only subscribe to paths you need
|
||||
5. **Clean Up Subscriptions** - Always call unsubscribe when done
|
||||
|
||||
---
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- State updates are synchronous and immediate
|
||||
- Subscriptions are called synchronously (be careful with expensive operations)
|
||||
- Persistence is debounced internally
|
||||
- Cross-tab sync uses BroadcastChannel (efficient)
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 38+
|
||||
- **Firefox**: 38+
|
||||
- **Safari**: 15.4+
|
||||
- **Mobile**: iOS 15.4+, Android Chrome 38+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- BroadcastChannel (for cross-tab sync)
|
||||
- localStorage/sessionStorage (for persistence)
|
||||
|
||||
---
|
||||
|
||||
**Next**: [Validation Module](validation.md) →
|
||||
|
||||
462
docs/modules/validation.md
Normal file
462
docs/modules/validation.md
Normal file
@@ -0,0 +1,462 @@
|
||||
# Validation Module
|
||||
|
||||
**Standalone Validation System for Fields, Forms, and Data**
|
||||
|
||||
The Validation Module provides a comprehensive validation system that can be used independently or integrated with form-handling and LiveComponents.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Schema-Based Validation** - Define validation rules in a schema
|
||||
- **Field-Level Validation** - Validate individual fields
|
||||
- **Async Validation** - Support for asynchronous validation (e.g., API checks)
|
||||
- **Custom Validation Rules** - Register your own validation rules
|
||||
- **Integration with form-handling** - Works seamlessly with form-handling module
|
||||
- **Integration with LiveComponents** - Validate LiveComponent data
|
||||
- **HTML5 Attribute Support** - Automatically reads validation rules from HTML attributes
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```javascript
|
||||
import { Validator } from './modules/validation/index.js';
|
||||
|
||||
// Create validator with schema
|
||||
const validator = Validator.create({
|
||||
email: ['required', 'email'],
|
||||
age: [
|
||||
'required',
|
||||
'number',
|
||||
{ rule: 'min', options: { value: 18 } },
|
||||
{ rule: 'max', options: { value: 100 } }
|
||||
],
|
||||
password: [
|
||||
'required',
|
||||
{ rule: 'minLength', options: { value: 8 } }
|
||||
]
|
||||
});
|
||||
|
||||
// Validate data
|
||||
const result = await validator.validate({
|
||||
email: 'user@example.com',
|
||||
age: 25,
|
||||
password: 'secret123'
|
||||
});
|
||||
|
||||
if (result.valid) {
|
||||
console.log('Validation passed');
|
||||
} else {
|
||||
console.log('Validation errors:', result.errors);
|
||||
}
|
||||
```
|
||||
|
||||
### From HTML Form
|
||||
|
||||
```javascript
|
||||
import { Validator } from './modules/validation/index.js';
|
||||
|
||||
// Create validator from form element
|
||||
const form = document.querySelector('#my-form');
|
||||
const validator = Validator.fromForm(form);
|
||||
|
||||
// Validate form data
|
||||
const formData = new FormData(form);
|
||||
const data = Object.fromEntries(formData);
|
||||
const result = await validator.validate(data);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
### Validator.create(schema)
|
||||
|
||||
Create a new Validator instance with a schema.
|
||||
|
||||
**Parameters**:
|
||||
- `schema` - Validation schema object
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const validator = Validator.create({
|
||||
email: 'email',
|
||||
age: ['required', 'number', { rule: 'min', options: { value: 18 } }]
|
||||
});
|
||||
```
|
||||
|
||||
### Validator.fromForm(form)
|
||||
|
||||
Create a validator from an HTML form element, reading validation rules from HTML attributes.
|
||||
|
||||
**Parameters**:
|
||||
- `form` - HTMLFormElement
|
||||
|
||||
**Example**:
|
||||
```html
|
||||
<form id="my-form">
|
||||
<input type="email" name="email" required />
|
||||
<input type="number" name="age" min="18" max="100" required />
|
||||
<input type="password" name="password" minlength="8" required />
|
||||
</form>
|
||||
```
|
||||
|
||||
```javascript
|
||||
const form = document.querySelector('#my-form');
|
||||
const validator = Validator.fromForm(form);
|
||||
```
|
||||
|
||||
### validator.validate(data)
|
||||
|
||||
Validate entire data object against schema.
|
||||
|
||||
**Parameters**:
|
||||
- `data` - Data object to validate
|
||||
|
||||
**Returns**: `Promise<ValidationResults>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const result = await validator.validate({
|
||||
email: 'user@example.com',
|
||||
age: 25
|
||||
});
|
||||
|
||||
console.log(result.valid); // true or false
|
||||
console.log(result.errors); // { email: ['...'], age: ['...'] }
|
||||
```
|
||||
|
||||
### validator.validateField(fieldName, value)
|
||||
|
||||
Validate a single field.
|
||||
|
||||
**Parameters**:
|
||||
- `fieldName` - Field name
|
||||
- `value` - Field value
|
||||
|
||||
**Returns**: `Promise<ValidationResult>`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
const result = await validator.validateField('email', 'user@example.com');
|
||||
console.log(result.valid); // true or false
|
||||
console.log(result.errors); // ['error message']
|
||||
```
|
||||
|
||||
### validator.validateFields(data, fieldNames)
|
||||
|
||||
Validate specific fields.
|
||||
|
||||
**Parameters**:
|
||||
- `data` - Data object
|
||||
- `fieldNames` - Array of field names to validate
|
||||
|
||||
**Returns**: `Promise<ValidationResults>`
|
||||
|
||||
### validator.registerRule(name, rule)
|
||||
|
||||
Register a custom validation rule.
|
||||
|
||||
**Parameters**:
|
||||
- `name` - Rule name
|
||||
- `rule` - Validation function: `(value, options) => boolean | string`
|
||||
|
||||
**Example**:
|
||||
```javascript
|
||||
validator.registerRule('customRule', (value, options) => {
|
||||
if (value === options.expected) {
|
||||
return true;
|
||||
}
|
||||
return options.message || 'Validation failed';
|
||||
});
|
||||
|
||||
// Use custom rule
|
||||
const validator = Validator.create({
|
||||
field: { rule: 'customRule', options: { expected: 'test', message: 'Must be "test"' } }
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Built-in Validation Rules
|
||||
|
||||
### required
|
||||
|
||||
Field must have a value.
|
||||
|
||||
```javascript
|
||||
email: 'required'
|
||||
// or
|
||||
email: { rule: 'required', options: { message: 'Email is required' } }
|
||||
```
|
||||
|
||||
### email
|
||||
|
||||
Valid email address format.
|
||||
|
||||
```javascript
|
||||
email: 'email'
|
||||
```
|
||||
|
||||
### url
|
||||
|
||||
Valid URL format.
|
||||
|
||||
```javascript
|
||||
website: 'url'
|
||||
```
|
||||
|
||||
### min / max
|
||||
|
||||
Minimum/maximum numeric value.
|
||||
|
||||
```javascript
|
||||
age: [
|
||||
{ rule: 'min', options: { value: 18 } },
|
||||
{ rule: 'max', options: { value: 100 } }
|
||||
]
|
||||
```
|
||||
|
||||
### minLength / maxLength
|
||||
|
||||
Minimum/maximum string length.
|
||||
|
||||
```javascript
|
||||
password: [
|
||||
{ rule: 'minLength', options: { value: 8 } },
|
||||
{ rule: 'maxLength', options: { value: 128 } }
|
||||
]
|
||||
```
|
||||
|
||||
### pattern
|
||||
|
||||
Regex pattern validation.
|
||||
|
||||
```javascript
|
||||
username: {
|
||||
rule: 'pattern',
|
||||
options: {
|
||||
value: '^[a-zA-Z0-9_]+$',
|
||||
message: 'Username can only contain letters, numbers, and underscores'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### number
|
||||
|
||||
Must be a valid number.
|
||||
|
||||
```javascript
|
||||
price: 'number'
|
||||
```
|
||||
|
||||
### integer
|
||||
|
||||
Must be an integer.
|
||||
|
||||
```javascript
|
||||
quantity: 'integer'
|
||||
```
|
||||
|
||||
### phone
|
||||
|
||||
Valid phone number format.
|
||||
|
||||
```javascript
|
||||
phone: 'phone'
|
||||
```
|
||||
|
||||
### postalCode
|
||||
|
||||
Valid postal code (supports DE, US, UK, FR).
|
||||
|
||||
```javascript
|
||||
postalCode: {
|
||||
rule: 'postalCode',
|
||||
options: { country: 'DE' }
|
||||
}
|
||||
```
|
||||
|
||||
### custom
|
||||
|
||||
Custom validation function.
|
||||
|
||||
```javascript
|
||||
field: {
|
||||
rule: 'custom',
|
||||
options: {
|
||||
validator: (value, options) => {
|
||||
// Custom validation logic
|
||||
return value === options.expected ? true : 'Value must be ' + options.expected;
|
||||
},
|
||||
expected: 'test'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Async Validation
|
||||
|
||||
```javascript
|
||||
const validator = Validator.create({
|
||||
email: {
|
||||
rule: 'custom',
|
||||
async: true,
|
||||
validator: async (value, options) => {
|
||||
// Check if email exists via API
|
||||
const response = await fetch(`/api/check-email?email=${value}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.exists) {
|
||||
return 'Email already exists';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const result = await validator.validate({ email: 'user@example.com' });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with form-handling
|
||||
|
||||
```javascript
|
||||
import { Validator } from './modules/validation/index.js';
|
||||
import { FormHandler } from './modules/form-handling/index.js';
|
||||
|
||||
const form = document.querySelector('#my-form');
|
||||
const validator = Validator.fromForm(form);
|
||||
const formHandler = FormHandler.create(form);
|
||||
|
||||
// Use validator with form handler
|
||||
formHandler.validator = validator;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration with LiveComponents
|
||||
|
||||
```javascript
|
||||
import { Validator } from './modules/validation/index.js';
|
||||
import { LiveComponentManager } from './modules/livecomponent/index.js';
|
||||
|
||||
const validator = Validator.create({
|
||||
email: ['required', 'email'],
|
||||
name: ['required', { rule: 'minLength', options: { value: 2 } }]
|
||||
});
|
||||
|
||||
// Validate before action
|
||||
const lcManager = LiveComponentManager.getInstance();
|
||||
lcManager.on('action:before-execute', async (componentId, actionName, params) => {
|
||||
if (actionName === 'submitForm') {
|
||||
const result = await validator.validate(params);
|
||||
if (!result.valid) {
|
||||
// Handle validation errors
|
||||
return false; // Prevent action
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Cases
|
||||
|
||||
### Form Validation
|
||||
|
||||
```javascript
|
||||
const validator = Validator.create({
|
||||
email: ['required', 'email'],
|
||||
password: [
|
||||
'required',
|
||||
{ rule: 'minLength', options: { value: 8 } }
|
||||
],
|
||||
confirmPassword: [
|
||||
'required',
|
||||
{
|
||||
rule: 'custom',
|
||||
validator: (value, options) => {
|
||||
const password = options.password;
|
||||
return value === password ? true : 'Passwords do not match';
|
||||
},
|
||||
options: { password: formData.password }
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### API Response Validation
|
||||
|
||||
```javascript
|
||||
const validator = Validator.create({
|
||||
id: ['required', 'integer'],
|
||||
name: ['required', { rule: 'minLength', options: { value: 1 } }],
|
||||
email: ['required', 'email']
|
||||
});
|
||||
|
||||
// Validate API response
|
||||
const response = await fetch('/api/user');
|
||||
const data = await response.json();
|
||||
const result = await validator.validate(data);
|
||||
|
||||
if (!result.valid) {
|
||||
console.error('Invalid API response:', result.errors);
|
||||
}
|
||||
```
|
||||
|
||||
### User Input Validation
|
||||
|
||||
```javascript
|
||||
// Validate on input
|
||||
const validator = Validator.create({
|
||||
search: {
|
||||
rule: 'minLength',
|
||||
options: { value: 3, message: 'Search must be at least 3 characters' }
|
||||
}
|
||||
});
|
||||
|
||||
const input = document.querySelector('#search');
|
||||
input.addEventListener('input', async (e) => {
|
||||
const result = await validator.validateField('search', e.target.value);
|
||||
if (!result.valid) {
|
||||
showError(result.errors[0]);
|
||||
} else {
|
||||
hideError();
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use Schema-Based Validation** - Define validation rules in a schema for reusability
|
||||
2. **Register Custom Rules** - Create reusable custom validation rules
|
||||
3. **Validate Early** - Validate on blur or input for better UX
|
||||
4. **Show Clear Errors** - Display validation errors clearly to users
|
||||
5. **Use Async Validation Sparingly** - Only for necessary checks (e.g., email uniqueness)
|
||||
|
||||
---
|
||||
|
||||
## Browser Support
|
||||
|
||||
- **Chrome/Edge**: 90+
|
||||
- **Firefox**: 88+
|
||||
- **Safari**: 14+
|
||||
- **Mobile**: iOS 14+, Android Chrome 90+
|
||||
|
||||
**Required Features**:
|
||||
- ES2020 JavaScript
|
||||
- Promise support
|
||||
- Async/await support
|
||||
|
||||
---
|
||||
|
||||
**Next**: [Error Tracking Module](error-tracking.md) →
|
||||
|
||||
140
docs/planning/ADMIN_AUTH_SECURITY_ENHANCEMENT.md
Normal file
140
docs/planning/ADMIN_AUTH_SECURITY_ENHANCEMENT.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Admin Auth Security Enhancement Plan
|
||||
|
||||
**Status**: Geplant für später (nach Refactoring anderer Module)
|
||||
**Ansatz**: Starke Web-Authentifizierung ohne VPN
|
||||
|
||||
## Überblick
|
||||
|
||||
Erweitert das Auth-Modul für den Admin-Bereich mit mehrschichtiger Web-Authentifizierung. Fokus auf starke Authentifizierung mit MFA, Session-Sicherheit und Rate Limiting.
|
||||
|
||||
## Sicherheitsebenen
|
||||
|
||||
1. **Obligatorisches MFA (TOTP)** - Zweiter Faktor zwingend erforderlich
|
||||
2. **Strenge Session-Sicherheit** - Regeneration, IP-Tracking, Timeouts
|
||||
3. **Rate Limiting & Account Lockout** - Schutz gegen Brute-Force
|
||||
4. **IP-basierte Einschränkungen** (optional) - Zusätzliche Barriere ohne VPN
|
||||
5. **Security Headers** - CSRF-Schutz, Secure Cookies, HSTS
|
||||
|
||||
## Aktuelle Situation
|
||||
|
||||
- Basis-Authentifizierung mit Session-Management vorhanden
|
||||
- MFA/TOTP-Services existieren (`TotpService`, `MfaService`), aber nicht vollständig integriert
|
||||
- IP-basierte Einschränkungen existieren (`ProductionSecurityMiddleware`)
|
||||
- AuthMiddleware ist derzeit deaktiviert
|
||||
- RouteAuthorizationService existiert, aber nicht konfiguriert
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Auth-Modul Erweiterung
|
||||
|
||||
1. **AdminAuthService** erstellen
|
||||
- Integration mit bestehendem `AuthenticationService`
|
||||
- Admin-spezifische Authentifizierungslogik
|
||||
- Session-Management für Admin-Bereich
|
||||
- IP-Tracking und Session-Regeneration
|
||||
- MFA-Status-Prüfung
|
||||
|
||||
2. **MFA-Integration** für Admin-Bereich
|
||||
- TOTP als obligatorische zweite Faktor über `MfaService`/`TotpProvider`
|
||||
- QR-Code-Generierung für Setup
|
||||
- Backup-Codes für Recovery
|
||||
- MFA-Enforcement für alle Admin-Routen
|
||||
|
||||
3. **Session-Sicherheit** verbessern
|
||||
- Session-Regeneration bei Login
|
||||
- IP-Konsistenz-Prüfung (warnen bei IP-Wechsel)
|
||||
- Session-Timeouts für Admin-Bereich (kürzer als normale Sessions)
|
||||
- Session-Fixation-Schutz
|
||||
- Secure Cookie-Flags (HttpOnly, Secure, SameSite)
|
||||
|
||||
### Phase 2: Route Protection & Authorization
|
||||
|
||||
1. **RouteAuthorizationService** aktivieren und konfigurieren
|
||||
- Admin-Namespace (`App\Application\Admin\*`) konfigurieren
|
||||
- IP-basierte Zugriffskontrolle (optional, aber empfohlen)
|
||||
- MFA-Status-Prüfung für alle Admin-Routen
|
||||
|
||||
2. **AuthMiddleware** implementieren
|
||||
- Integration mit `RouteAuthorizationService`
|
||||
- Session-Validierung
|
||||
- MFA-Status-Prüfung
|
||||
- Redirect zu Login bei fehlender Authentifizierung
|
||||
- Redirect zu MFA-Setup bei fehlender MFA-Konfiguration
|
||||
|
||||
3. **AdminLoginController** erstellen
|
||||
- Login-Formular
|
||||
- MFA-Setup-Flow (wenn noch nicht konfiguriert)
|
||||
- MFA-Verifizierung nach initialem Login
|
||||
- Session-Erstellung
|
||||
- Logout-Funktionalität
|
||||
|
||||
### Phase 3: Erweiterte Sicherheitsfeatures
|
||||
|
||||
1. **IP-basierte Einschränkungen** (Optional, aber empfohlen)
|
||||
- Statische IP-Whitelist für Admin-Zugriff
|
||||
- CIDR-Notation für IP-Ranges unterstützen
|
||||
- Konfigurierbar über Environment-Variablen
|
||||
- Integration mit `ProductionSecurityMiddleware`
|
||||
|
||||
2. **Rate Limiting** für Admin-Bereich
|
||||
- Striktere Limits als für normale User
|
||||
- IP-basierte Rate Limits
|
||||
- Account-Lockout nach fehlgeschlagenen Versuchen
|
||||
- Separate Limits für Login, MFA-Verifizierung
|
||||
|
||||
3. **Security Headers**
|
||||
- CSRF-Schutz für alle Admin-Formulare
|
||||
- Secure Cookies (HTTPS-only)
|
||||
- HSTS für Admin-Bereich
|
||||
- Content-Security-Policy
|
||||
|
||||
## Dateien die erstellt/geändert werden müssen
|
||||
|
||||
### Neue Dateien
|
||||
- `src/Framework/Auth/AdminAuthService.php` - Admin-spezifischer Auth-Service
|
||||
- `src/Application/Admin/Auth/AdminLoginController.php` - Login-Controller
|
||||
- `src/Framework/Auth/MfaRequiredException.php` - Exception für fehlende MFA
|
||||
- `src/Framework/Auth/AdminMfaService.php` - MFA-Service für Admin-Bereich
|
||||
- `src/Framework/Auth/AdminMfaSetupResult.php` - Result Value Objects
|
||||
- `src/Framework/Auth/AdminMfaActivationResult.php`
|
||||
- `src/Framework/Auth/AdminMfaVerificationResult.php`
|
||||
- `tests/Framework/Auth/AdminAuthServiceTest.php` - Tests
|
||||
- `tests/Framework/Auth/AdminMfaServiceTest.php` - MFA-Tests
|
||||
|
||||
### Zu erweiternde Dateien
|
||||
- `src/Framework/Http/Middlewares/AuthMiddleware.php` - Vollständige Implementierung
|
||||
- `src/Framework/Auth/RouteAuthorizationServiceInitializer.php` - Konfiguration aktivieren
|
||||
- `src/Framework/Auth/AuthenticationService.php` - MFA-Integration
|
||||
- `src/Framework/Http/Middlewares/ProductionSecurityMiddleware.php` - IP-Whitelist-Erweiterung
|
||||
- `src/Framework/Config/EnvKey.php` - Environment-Keys hinzufügen
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Environment-Variablen
|
||||
- `ADMIN_MFA_REQUIRED=true` - MFA obligatorisch
|
||||
- `ADMIN_SESSION_TIMEOUT=1800` - Session-Timeout (30 Min)
|
||||
- `ADMIN_IP_WHITELIST` - Optionale IP-Whitelist (komma-separiert, CIDR unterstützt)
|
||||
- `ADMIN_RATE_LIMIT_ATTEMPTS=3` - Strikte Rate Limits
|
||||
- `ADMIN_ACCOUNT_LOCKOUT_DURATION=3600` - Lockout nach Fehlversuchen (1h)
|
||||
- `ADMIN_SESSION_REGENERATE_ON_LOGIN=true` - Session-Regeneration
|
||||
- `ADMIN_CHECK_IP_CONSISTENCY=true` - IP-Wechsel überwachen
|
||||
|
||||
### MFA-Integration
|
||||
- Verwende `MfaService` mit `TotpProvider` (nicht direkt `TotpService`)
|
||||
- `MfaMethod::TOTP` für TOTP-Authentifizierung
|
||||
- `MfaSecret`, `MfaChallenge`, `MfaCode` Value Objects verwenden
|
||||
|
||||
## Wichtige Hinweise
|
||||
|
||||
- **Kein VPN**: Lösung basiert auf starker Web-Authentifizierung ohne VPN
|
||||
- **MFA obligatorisch**: Alle Admin-Routen erfordern konfigurierte und verifizierte MFA
|
||||
- **Audit-Logging**: Wird später implementiert, wenn eine vollständige Audit-Lösung im Framework vorhanden ist
|
||||
- **IP-Whitelist**: Optional, aber empfohlen für Production
|
||||
|
||||
## Referenzen
|
||||
|
||||
- MFA-Modul: `src/Framework/Mfa/`
|
||||
- TotpProvider: `src/Framework/Mfa/Providers/TotpProvider.php`
|
||||
- RouteAuthorizationService: `src/Framework/Auth/RouteAuthorizationService.php`
|
||||
- ProductionSecurityMiddleware: `src/Framework/Http/Middlewares/ProductionSecurityMiddleware.php`
|
||||
|
||||
186
docs/planning/CLEANUP_PLAN.md
Normal file
186
docs/planning/CLEANUP_PLAN.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Root Directory Cleanup Plan
|
||||
|
||||
## 1. Files to Move
|
||||
|
||||
### Debug Scripts → scripts/debug/
|
||||
```bash
|
||||
mv debug_*.php scripts/debug/
|
||||
mv test_*.php scripts/test/
|
||||
mv simple_debug_tui.php scripts/debug/
|
||||
mv populate_images_from_filesystem.php scripts/maintenance/
|
||||
mv websocket.php scripts/test/
|
||||
```
|
||||
|
||||
### Documentation → docs/
|
||||
```bash
|
||||
# Root markdown files to consolidate
|
||||
mv AUTOLOADER_WORKAROUND.md docs/troubleshooting/
|
||||
mv DEPLOYMENT*.md docs/deployment/
|
||||
mv SSL-PRODUCTION-SETUP.md docs/deployment/
|
||||
mv PRODUCTION-SECURITY-UPDATES.md docs/deployment/
|
||||
mv README-*.md docs/guides/
|
||||
mv TODO.md docs/
|
||||
mv docs-*.md docs/planning/
|
||||
```
|
||||
|
||||
### Public Directory Security Cleanup
|
||||
```bash
|
||||
# REMOVE from public/ (move to scripts/debug/)
|
||||
mv public/debug.php scripts/debug/
|
||||
mv public/test.php scripts/test/
|
||||
mv public/security-test.php scripts/debug/
|
||||
mv public/production-test.php scripts/debug/
|
||||
mv public/quick-fix.php scripts/debug/
|
||||
mv public/build-container.php scripts/debug/
|
||||
mv public/force-production-test.php scripts/debug/
|
||||
mv public/dev-hot-reload*.php scripts/debug/
|
||||
mv public/minimal.php scripts/debug/
|
||||
|
||||
# public/ should only contain:
|
||||
# - index.php (production entry point)
|
||||
# - health.php (monitoring)
|
||||
# - .vite/ (build artifacts)
|
||||
# - assets/ (compiled assets)
|
||||
```
|
||||
|
||||
## 2. New Directory Structure
|
||||
|
||||
```
|
||||
michaelschiemer/
|
||||
├── bin/ # Executable scripts
|
||||
│ ├── console # Symlink to console.php
|
||||
│ └── worker # Symlink to worker.php
|
||||
├── config/ # Configuration files
|
||||
│ └── static-routes.json
|
||||
├── docs/ # Consolidated documentation
|
||||
│ ├── README.md
|
||||
│ ├── architecture/
|
||||
│ │ ├── framework-principles.md
|
||||
│ │ ├── di-container.md
|
||||
│ │ └── discovery-system.md
|
||||
│ ├── deployment/
|
||||
│ │ ├── production-setup.md
|
||||
│ │ ├── ssl-configuration.md
|
||||
│ │ └── docker-guide.md
|
||||
│ ├── guides/
|
||||
│ │ ├── getting-started.md
|
||||
│ │ ├── api-versioning.md
|
||||
│ │ └── testing.md
|
||||
│ ├── troubleshooting/
|
||||
│ │ ├── autoloader-workaround.md
|
||||
│ │ └── common-issues.md
|
||||
│ └── planning/
|
||||
│ └── TODO.md
|
||||
├── public/ # Web-accessible (MINIMAL!)
|
||||
│ ├── index.php
|
||||
│ ├── health.php
|
||||
│ └── .vite/
|
||||
├── resources/ # Source assets
|
||||
│ ├── css/
|
||||
│ └── js/
|
||||
├── scripts/ # Development & maintenance
|
||||
│ ├── debug/ # Debug scripts (NOT web-accessible)
|
||||
│ ├── test/ # Test scripts
|
||||
│ ├── deployment/ # Deployment scripts
|
||||
│ └── maintenance/ # Maintenance scripts
|
||||
├── src/ # Application source
|
||||
├── storage/ # Runtime data
|
||||
│ ├── cache/ # Add to .gitignore
|
||||
│ ├── logs/
|
||||
│ └── uploads/
|
||||
├── tests/ # Test suite
|
||||
├── vendor/ # Composer dependencies
|
||||
└── var/ # Temporary files
|
||||
└── phpstan/cache/ # Add to .gitignore
|
||||
```
|
||||
|
||||
## 3. Gitignore Updates
|
||||
|
||||
Add to `.gitignore`:
|
||||
```
|
||||
# Cache
|
||||
storage/cache/*.cache.php
|
||||
storage/cache/*.php
|
||||
|
||||
# PHPStan
|
||||
var/phpstan/cache/**
|
||||
|
||||
# Logs
|
||||
storage/logs/*.log
|
||||
|
||||
# Temporary debug files
|
||||
scripts/debug/output/
|
||||
scripts/test/output/
|
||||
```
|
||||
|
||||
## 4. Immediate Actions
|
||||
|
||||
### Priority 1: Security (DO IMMEDIATELY!)
|
||||
```bash
|
||||
# Remove debug files from public/
|
||||
rm public/debug.php
|
||||
rm public/test.php
|
||||
rm public/security-test.php
|
||||
rm public/production-test.php
|
||||
rm public/quick-fix.php
|
||||
rm public/build-container.php
|
||||
rm public/force-production-test.php
|
||||
rm public/dev-hot-reload.php
|
||||
rm public/dev-hot-reload-minimal.php
|
||||
rm public/minimal.php
|
||||
```
|
||||
|
||||
### Priority 2: Cache Cleanup
|
||||
```bash
|
||||
# Clear old cache files
|
||||
find storage/cache -name "*.cache.php" -mtime +7 -delete
|
||||
find var/phpstan/cache -type f -mtime +7 -delete
|
||||
```
|
||||
|
||||
### Priority 3: Documentation Consolidation
|
||||
```bash
|
||||
# Create new structure
|
||||
mkdir -p docs/{architecture,deployment,guides,troubleshooting,planning}
|
||||
mkdir -p scripts/{debug,test,deployment,maintenance}
|
||||
|
||||
# Move files (execute moves from section 1)
|
||||
```
|
||||
|
||||
## 5. Benefits
|
||||
|
||||
- ✅ **Security**: No debug code in public/
|
||||
- ✅ **Organization**: Clear separation of concerns
|
||||
- ✅ **Performance**: Cleaner cache structure
|
||||
- ✅ **Developer Experience**: Easy navigation
|
||||
- ✅ **Professional**: Clean root directory
|
||||
- ✅ **Maintainability**: Consolidated documentation
|
||||
|
||||
## 6. Automated Cleanup Script
|
||||
|
||||
Create `scripts/maintenance/cleanup-project.php`:
|
||||
```php
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
// Automated cleanup script
|
||||
// - Clear old cache files
|
||||
// - Remove temporary files
|
||||
// - Validate directory structure
|
||||
```
|
||||
|
||||
## Execution Timeline
|
||||
|
||||
**Week 1**:
|
||||
- Security cleanup (public/ directory)
|
||||
- Cache cleanup
|
||||
|
||||
**Week 2**:
|
||||
- Documentation consolidation
|
||||
- Root directory organization
|
||||
|
||||
**Week 3**:
|
||||
- Script organization
|
||||
- Automated cleanup tools
|
||||
|
||||
**Week 4**:
|
||||
- Validation & testing
|
||||
- Update documentation
|
||||
160
docs/planning/LIVECOMPONENTS_TEST.md
Normal file
160
docs/planning/LIVECOMPONENTS_TEST.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# 🧪 LiveComponents Test Guide
|
||||
|
||||
## Quick Test
|
||||
|
||||
1. **Start Server**:
|
||||
```bash
|
||||
make up
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. **Open Test Page**:
|
||||
```
|
||||
https://localhost/test/livecomponents
|
||||
```
|
||||
|
||||
3. **Test Counter Component**:
|
||||
- ✅ Click **+ Increment** → Count increases
|
||||
- ✅ Click **- Decrement** → Count decreases
|
||||
- ✅ Click **Reset** → Count resets to 0
|
||||
- ✅ Enter **5** and click **Add Amount** → Count +5
|
||||
- ✅ Wait 10 seconds → Auto-update (polling)
|
||||
|
||||
## What's Being Tested
|
||||
|
||||
### ✅ Core Features
|
||||
- **Action Handling** - Button clicks execute component methods
|
||||
- **Form Submission** - Forms with parameters work
|
||||
- **State Management** - Component data persists and updates
|
||||
- **Polling** - Auto-updates every 10 seconds
|
||||
- **DOM Updates** - HTML re-renders on state change
|
||||
|
||||
### ✅ Framework Patterns
|
||||
- **Trait + Interface** - No abstract classes
|
||||
- **Readonly Classes** - Immutable components
|
||||
- **Value Objects** - Type-safe data handling
|
||||
- **Dependency Injection** - TemplateRenderer injection
|
||||
|
||||
### ✅ JavaScript
|
||||
- **Zero Dependencies** - Pure Vanilla JS
|
||||
- **Auto-initialization** - Finds components on page load
|
||||
- **Event Handling** - Buttons, forms, polling
|
||||
- **Progress Tracking** - Upload progress (ready for file uploads)
|
||||
|
||||
## Test Files
|
||||
|
||||
```
|
||||
src/
|
||||
├── Application/
|
||||
│ ├── Controllers/Test/
|
||||
│ │ ├── LiveComponentTestController.php ← Test Route
|
||||
│ │ └── README.md ← Test Documentation
|
||||
│ └── Components/
|
||||
│ └── CounterComponent.php ← Test Component
|
||||
├── Framework/LiveComponents/
|
||||
│ └── Templates/
|
||||
│ └── counter.view.php ← Component Template
|
||||
└── resources/views/test/
|
||||
└── livecomponents.view.php ← Test Page
|
||||
```
|
||||
|
||||
## Browser Console Output
|
||||
|
||||
Expected console output:
|
||||
```javascript
|
||||
LiveComponents Test Suite Loaded
|
||||
Available: {
|
||||
liveComponents: LiveComponentManager,
|
||||
sseManager: SSEManager
|
||||
}
|
||||
```
|
||||
|
||||
## Network Requests
|
||||
|
||||
Watch for:
|
||||
```
|
||||
POST /live-component/App\Application\Components\CounterComponent:demo
|
||||
|
||||
Request:
|
||||
{
|
||||
"component_id": "App\\Application\\Components\\CounterComponent:demo",
|
||||
"method": "increment",
|
||||
"params": {},
|
||||
"state": { "count": 0 }
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"html": "<div>...</div>",
|
||||
"events": [],
|
||||
"state": "{\"id\":\"...\",\"component\":\"...\",\"data\":{\"count\":1}}"
|
||||
}
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
### ❌ Component not initializing?
|
||||
```javascript
|
||||
// Check if LiveComponents loaded
|
||||
console.log(window.liveComponents);
|
||||
|
||||
// Check component registered
|
||||
console.log(window.liveComponents.components);
|
||||
```
|
||||
|
||||
### ❌ Actions not working?
|
||||
```javascript
|
||||
// Manual action call
|
||||
window.liveComponents.callAction(
|
||||
'App\\Application\\Components\\CounterComponent:demo',
|
||||
'increment',
|
||||
{}
|
||||
);
|
||||
```
|
||||
|
||||
### ❌ Polling not working?
|
||||
```javascript
|
||||
// Check polling interval
|
||||
document.querySelector('[data-poll-interval]').dataset.pollInterval;
|
||||
|
||||
// Force poll
|
||||
window.liveComponents.callAction('CounterComponent:demo', 'poll', {});
|
||||
```
|
||||
|
||||
## Next: Add Your Own Component
|
||||
|
||||
```php
|
||||
// 1. Create Component
|
||||
final readonly class MyComponent implements LiveComponentContract
|
||||
{
|
||||
use LiveComponentTrait;
|
||||
|
||||
public function __construct(string $id, array $initialData = [], ?TemplateRenderer $templateRenderer = null) {
|
||||
$this->id = $id;
|
||||
$this->initialData = $initialData;
|
||||
$this->templateRenderer = $templateRenderer;
|
||||
}
|
||||
|
||||
public function render(): string {
|
||||
return $this->template('Framework/LiveComponents/Templates/my-component', []);
|
||||
}
|
||||
|
||||
public function myAction(): array {
|
||||
return ['updated' => true];
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Add to LiveComponentTestController
|
||||
$myComponent = new MyComponent(
|
||||
id: ComponentRegistry::makeId(MyComponent::class, 'test')
|
||||
);
|
||||
|
||||
// 3. Render in view
|
||||
{!! myComponent.toHtml() !!}
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- 📚 Full docs: `/docs/claude/livecomponents-system.md`
|
||||
- 📦 Module README: `/src/Framework/LiveComponents/README.md`
|
||||
- 🧪 Test README: `/src/Application/Controllers/Test/README.md`
|
||||
368
docs/planning/SRC_STRUCTURE_IMPROVEMENTS.md
Normal file
368
docs/planning/SRC_STRUCTURE_IMPROVEMENTS.md
Normal file
@@ -0,0 +1,368 @@
|
||||
# Src Directory Structure Improvements
|
||||
|
||||
## Current Issues
|
||||
|
||||
### 1. Application Layer Fragmentation
|
||||
```
|
||||
src/Application/
|
||||
├── Admin/ # Admin features
|
||||
├── Api/ # API endpoints
|
||||
├── Auth/ # Authentication
|
||||
├── Backend/ # Backend integrations?
|
||||
├── Campaign/ # Campaign management
|
||||
├── Controller/ # Generic controllers?
|
||||
├── Design/ # Design system?
|
||||
├── Http/ # HTTP utilities
|
||||
├── Service/ # Generic services?
|
||||
├── Website/ # Website pages
|
||||
└── ... (20+ directories)
|
||||
```
|
||||
|
||||
**Problem**:
|
||||
- No clear separation between features and infrastructure
|
||||
- Mix of feature modules and technical layers
|
||||
- Hard to find related code
|
||||
|
||||
### 2. Framework Layer Organization
|
||||
```
|
||||
src/Framework/
|
||||
├── Async/
|
||||
├── AsyncExamples/ # Examples should not be in Framework/
|
||||
├── Cache/
|
||||
├── Database/
|
||||
├── DI/
|
||||
├── Discovery/
|
||||
└── ... (50+ directories)
|
||||
```
|
||||
|
||||
**Problem**:
|
||||
- Examples mixed with production code
|
||||
- Very deep nesting (Database/Schema/Index/Analysis/)
|
||||
- Some modules could be consolidated
|
||||
|
||||
### 3. Domain Layer Inconsistency
|
||||
```
|
||||
src/Domain/
|
||||
├── AI/ # Is this really domain?
|
||||
├── Common/ # Shared code
|
||||
├── Contact/
|
||||
├── Media/
|
||||
├── Meta/ # What is Meta domain?
|
||||
├── Newsletter/
|
||||
├── PreSave/ # Feature-specific
|
||||
├── SmartLink/
|
||||
└── User/
|
||||
```
|
||||
|
||||
**Problem**:
|
||||
- Mix of bounded contexts and shared code
|
||||
- Unclear domain boundaries
|
||||
- Technical concerns (AI) mixed with business domains
|
||||
|
||||
## Proposed Improvements
|
||||
|
||||
### A. Application Layer Restructuring
|
||||
|
||||
**Option 1: Feature-Based Modules** (RECOMMENDED)
|
||||
```
|
||||
src/Application/
|
||||
├── Admin/ # Admin Panel Feature
|
||||
│ ├── Analytics/
|
||||
│ ├── Content/
|
||||
│ ├── System/
|
||||
│ └── Controllers/
|
||||
├── Api/ # API Layer
|
||||
│ ├── V1/
|
||||
│ ├── V2/
|
||||
│ └── Middleware/
|
||||
├── Auth/ # Authentication Feature
|
||||
│ ├── Controllers/
|
||||
│ ├── Middleware/
|
||||
│ └── Services/
|
||||
├── Campaign/ # Campaign Management Feature
|
||||
│ ├── Controllers/
|
||||
│ ├── Services/
|
||||
│ └── ValueObjects/
|
||||
├── Website/ # Public Website Feature
|
||||
│ ├── Controllers/
|
||||
│ ├── Services/
|
||||
│ └── templates/
|
||||
└── Shared/ # Application-wide shared code
|
||||
├── Controllers/ # Base controllers
|
||||
├── Middleware/
|
||||
└── Services/
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Clear feature boundaries
|
||||
- Related code grouped together
|
||||
- Easy to find and navigate
|
||||
- Follows Vertical Slice Architecture
|
||||
|
||||
**Option 2: Layer-Based Organization**
|
||||
```
|
||||
src/Application/
|
||||
├── Controllers/ # All controllers
|
||||
│ ├── Admin/
|
||||
│ ├── Api/
|
||||
│ ├── Auth/
|
||||
│ └── Website/
|
||||
├── Services/ # All services
|
||||
├── Middleware/ # All middleware
|
||||
└── ValueObjects/ # Application VOs
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Technical separation
|
||||
- Easy to see all controllers/services
|
||||
- Simpler structure
|
||||
|
||||
**Downside**: Harder to see complete features
|
||||
|
||||
### B. Framework Layer Improvements
|
||||
|
||||
**Clean Up Examples**:
|
||||
```bash
|
||||
# Move examples OUT of src/Framework/
|
||||
src/
|
||||
├── Framework/ # Production framework code
|
||||
│ ├── Cache/
|
||||
│ ├── Database/
|
||||
│ ├── DI/
|
||||
│ └── ...
|
||||
└── Examples/ # All examples here
|
||||
├── Async/
|
||||
├── Cache/
|
||||
├── Database/
|
||||
└── GraphQL/
|
||||
```
|
||||
|
||||
**Consolidate Deep Nesting**:
|
||||
```
|
||||
src/Framework/Database/
|
||||
├── Connection/ # Consolidated connection handling
|
||||
│ ├── Async/
|
||||
│ ├── Middleware/
|
||||
│ ├── Pooled/
|
||||
│ └── ReadWrite/
|
||||
├── Migration/
|
||||
│ ├── Commands/
|
||||
│ ├── Runners/
|
||||
│ └── ValueObjects/
|
||||
├── Monitoring/
|
||||
│ ├── Health/
|
||||
│ ├── Profiling/
|
||||
│ └── Metrics/
|
||||
├── QueryBuilder/
|
||||
├── Repository/
|
||||
├── Schema/
|
||||
│ ├── Blueprint/
|
||||
│ ├── Comparison/
|
||||
│ └── Index/
|
||||
└── UnitOfWork/
|
||||
```
|
||||
|
||||
**Instead of**:
|
||||
```
|
||||
Database/
|
||||
Monitoring/
|
||||
Health/
|
||||
Checks/ # Too deep!
|
||||
```
|
||||
|
||||
### C. Domain Layer Restructuring
|
||||
|
||||
**Bounded Contexts Approach**:
|
||||
```
|
||||
src/Domain/
|
||||
├── BoundedContexts/ # Clear business domains
|
||||
│ ├── Campaign/
|
||||
│ │ ├── Entities/
|
||||
│ │ ├── ValueObjects/
|
||||
│ │ ├── Services/
|
||||
│ │ └── Repositories/
|
||||
│ ├── Contact/
|
||||
│ ├── Media/
|
||||
│ ├── Newsletter/
|
||||
│ ├── SmartLink/ # Renamed from PreSave
|
||||
│ └── User/
|
||||
├── Shared/ # Shared Kernel
|
||||
│ ├── ValueObjects/ # Cross-domain VOs
|
||||
│ ├── Interfaces/
|
||||
│ └── Exceptions/
|
||||
└── Services/ # Domain Services
|
||||
└── AI/ # AI as domain service
|
||||
```
|
||||
|
||||
**Benefits**:
|
||||
- Clear bounded context boundaries
|
||||
- Shared kernel explicit
|
||||
- Domain services separated
|
||||
- DDD-compliant structure
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1: Immediate Cleanup (Week 1)
|
||||
```bash
|
||||
# 1. Move examples out of Framework
|
||||
mkdir -p examples
|
||||
mv src/Framework/AsyncExamples examples/Async
|
||||
mv src/Framework/Database/Examples examples/Database
|
||||
# ... repeat for all examples
|
||||
|
||||
# 2. Update composer.json autoload
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "src/",
|
||||
"Examples\\": "examples/"
|
||||
}
|
||||
}
|
||||
|
||||
# 3. Regenerate autoloader
|
||||
composer dump-autoload
|
||||
```
|
||||
|
||||
### Phase 2: Documentation (Week 2)
|
||||
```bash
|
||||
# Create architecture docs
|
||||
docs/architecture/
|
||||
├── application-layer.md # Feature-based organization
|
||||
├── framework-layer.md # Framework structure
|
||||
├── domain-layer.md # Bounded contexts
|
||||
└── migration-guide.md # How to navigate new structure
|
||||
```
|
||||
|
||||
### Phase 3: Gradual Migration (Weeks 3-6)
|
||||
- Move Application code to feature modules (one at a time)
|
||||
- Consolidate Framework deep nesting
|
||||
- Restructure Domain bounded contexts
|
||||
- Update imports and tests
|
||||
|
||||
## Recommended Final Structure
|
||||
|
||||
```
|
||||
michaelschiemer/
|
||||
├── bin/
|
||||
├── config/
|
||||
├── docs/
|
||||
│ ├── architecture/
|
||||
│ ├── deployment/
|
||||
│ └── guides/
|
||||
├── examples/ # All framework examples
|
||||
│ ├── Async/
|
||||
│ ├── Cache/
|
||||
│ ├── Database/
|
||||
│ └── GraphQL/
|
||||
├── public/ # Minimal! Only index.php + health.php
|
||||
├── resources/
|
||||
│ ├── css/
|
||||
│ └── js/
|
||||
├── scripts/
|
||||
│ ├── debug/
|
||||
│ ├── test/
|
||||
│ ├── deployment/
|
||||
│ └── maintenance/
|
||||
├── src/
|
||||
│ ├── Application/ # Feature-based modules
|
||||
│ │ ├── Admin/
|
||||
│ │ ├── Api/
|
||||
│ │ ├── Auth/
|
||||
│ │ ├── Campaign/
|
||||
│ │ ├── Website/
|
||||
│ │ └── Shared/
|
||||
│ ├── Domain/ # Bounded contexts
|
||||
│ │ ├── BoundedContexts/
|
||||
│ │ │ ├── Campaign/
|
||||
│ │ │ ├── Contact/
|
||||
│ │ │ ├── Media/
|
||||
│ │ │ ├── Newsletter/
|
||||
│ │ │ ├── SmartLink/
|
||||
│ │ │ └── User/
|
||||
│ │ ├── Shared/
|
||||
│ │ └── Services/
|
||||
│ ├── Framework/ # Framework (production only)
|
||||
│ │ ├── Cache/
|
||||
│ │ ├── Console/
|
||||
│ │ ├── Database/
|
||||
│ │ ├── DI/
|
||||
│ │ ├── Discovery/
|
||||
│ │ ├── Http/
|
||||
│ │ └── ...
|
||||
│ └── Infrastructure/ # External integrations
|
||||
│ ├── GeoIp/
|
||||
│ └── ...
|
||||
├── storage/
|
||||
├── tests/ # Mirrors src/ structure
|
||||
│ ├── Application/
|
||||
│ ├── Domain/
|
||||
│ ├── Framework/
|
||||
│ └── Integration/
|
||||
├── var/
|
||||
└── vendor/
|
||||
|
||||
```
|
||||
|
||||
## Quality Metrics
|
||||
|
||||
**Before**:
|
||||
- Root files: 105
|
||||
- Public debug files: 9
|
||||
- Application directories: 25+
|
||||
- Framework nesting depth: 6 levels
|
||||
- Examples in production: Yes
|
||||
|
||||
**After**:
|
||||
- Root files: ~15 (essential only)
|
||||
- Public debug files: 0 (SECURITY!)
|
||||
- Application modules: ~8 (feature-based)
|
||||
- Framework nesting depth: 3-4 levels max
|
||||
- Examples location: Separate examples/ directory
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [ ] Move debug/test scripts to scripts/
|
||||
- [ ] Clean up public/ directory (SECURITY PRIORITY!)
|
||||
- [ ] Move examples out of src/Framework/
|
||||
- [ ] Create examples/ directory
|
||||
- [ ] Consolidate documentation in docs/
|
||||
- [ ] Restructure Application layer (feature-based)
|
||||
- [ ] Simplify Framework deep nesting
|
||||
- [ ] Organize Domain bounded contexts
|
||||
- [ ] Update composer autoload
|
||||
- [ ] Update all imports
|
||||
- [ ] Update tests to match new structure
|
||||
- [ ] Update .gitignore
|
||||
- [ ] Clear old cache files
|
||||
- [ ] Document new structure
|
||||
- [ ] Create navigation guide
|
||||
|
||||
## Tools to Create
|
||||
|
||||
### 1. Structure Validator
|
||||
```bash
|
||||
php console.php structure:validate
|
||||
|
||||
# Checks:
|
||||
# - No PHP files in public/ except index.php/health.php
|
||||
# - All examples in examples/ directory
|
||||
# - Cache size warnings
|
||||
# - Proper namespace structure
|
||||
```
|
||||
|
||||
### 2. Automatic Cleanup
|
||||
```bash
|
||||
php console.php cleanup:project
|
||||
|
||||
# Actions:
|
||||
# - Clear old cache files
|
||||
# - Remove temporary files
|
||||
# - Report orphaned files
|
||||
```
|
||||
|
||||
### 3. Migration Helper
|
||||
```bash
|
||||
php console.php migrate:structure --dry-run
|
||||
|
||||
# Shows what would be moved/changed
|
||||
# Then run without --dry-run to execute
|
||||
```
|
||||
112
docs/quality/code-quality-scanner.md
Normal file
112
docs/quality/code-quality-scanner.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Code Quality Scanner
|
||||
|
||||
This module provides repository-aware rules that validate architectural conventions. It currently ships with the following rules:
|
||||
|
||||
- `StringableImplementation`: classes that define `__toString()` must implement `Stringable`
|
||||
- `FinalClass`: value-object classes must be declared `final`
|
||||
- `ReadonlyClass`: value-object classes must be declared `readonly`
|
||||
- `ReadonlyGetter`: readonly properties (or classes) should be exposed directly; avoid `get*()` wrappers
|
||||
- `NoInheritance`: inheritance is forbidden (except for a small allow list, e.g. Exceptions)
|
||||
|
||||
## Command Line Usage
|
||||
|
||||
Run the scanner from the project root:
|
||||
|
||||
```bash
|
||||
php console.php quality:scan # scans src/ by default
|
||||
php console.php quality:scan app/path # scan a custom directory
|
||||
php console.php quality:scan --skip-errors # skip classes/files that cannot be parsed
|
||||
```
|
||||
|
||||
The command groups violations by rule and shows file locations:
|
||||
|
||||
```
|
||||
❌ Found 2 violation(s). Scanned 185 files, 174 classes in 6.91s
|
||||
|
||||
StringableImplementation (2)
|
||||
• App\Domain\User\ValueObjects\UserId
|
||||
↳ src/Domain/User/ValueObjects/UserId.php:42
|
||||
Classes defining __toString() must implement Stringable explicitly.
|
||||
```
|
||||
|
||||
The exit code is `0` when all checks pass, or `1` when violations are found. You can wire this command into CI to block regressions.
|
||||
|
||||
### Skipping Faulty Classes
|
||||
|
||||
Some parts of the repository (legacy examples, experimental prototypes, etc.) may not load because they contain invalid PHP constructs or depend on unavailable interfaces. Use the `--skip-errors` flag to collect a warning and continue scanning the remaining classes:
|
||||
|
||||
```bash
|
||||
php console.php quality:scan --skip-errors
|
||||
```
|
||||
|
||||
When the flag is omitted, any parsing/reflection error stops the scan and the command exits with a non-zero status.
|
||||
|
||||
## Adding New Rules
|
||||
|
||||
Rules implement `App\Framework\Quality\CodeQuality\CodeQualityRule`:
|
||||
|
||||
```php
|
||||
use App\Framework\Quality\CodeQuality\CodeQualityRule;
|
||||
use App\Framework\Quality\CodeQuality\Results\RuleViolation;
|
||||
use App\Framework\Quality\CodeQuality\ValueObjects\ClassInspection;
|
||||
|
||||
final class MyCustomRule implements CodeQualityRule
|
||||
{
|
||||
public function name(): string
|
||||
{
|
||||
return 'MyCustomRule';
|
||||
}
|
||||
|
||||
public function check(ClassInspection $inspection): array
|
||||
{
|
||||
if ($this->isValid($inspection)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$line = $inspection->getMethodLine('someMethod');
|
||||
|
||||
return [
|
||||
RuleViolation::create(
|
||||
$inspection->className(),
|
||||
$inspection->filePath(),
|
||||
$this->name(),
|
||||
'Explain what must change.',
|
||||
$line
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Register the rule inside `CodeQualityScanCommand`:
|
||||
|
||||
```php
|
||||
$this->scanner = new CodeQualityScanner(
|
||||
$fileScanner,
|
||||
$reflectionService,
|
||||
[
|
||||
new RequireStringableImplementationRule(),
|
||||
new MyCustomRule(),
|
||||
],
|
||||
$logger
|
||||
);
|
||||
```
|
||||
|
||||
Because rules leverage full reflection data and the actual file path, you have the flexibility to enforce namespacing, attribute usage, dependency boundaries, and more.
|
||||
|
||||
## Storage Permissions
|
||||
|
||||
The scanner runs inside the console bootstrap. When Redis is unavailable, the framework falls back to the filesystem cache. The cache directory now resolves to `<project-root>/storage/cache`, but you still need to make sure it exists and is writable:
|
||||
|
||||
```bash
|
||||
mkdir -p storage/cache
|
||||
chmod -R ug+rw storage
|
||||
```
|
||||
|
||||
If you run the command from another working directory (e.g. inside Docker), set the base path explicitly so the cache resolver finds the right storage folder:
|
||||
|
||||
```bash
|
||||
APP_BASE_PATH=/home/michael/dev/michaelschiemer php console.php quality:scan
|
||||
```
|
||||
|
||||
The CLI entry point already exports `APP_BASE_PATH`; keep the environment variable handy for custom runners, background workers, or integration tests.
|
||||
318
docs/todo/design-token-system-future-features.md
Normal file
318
docs/todo/design-token-system-future-features.md
Normal file
@@ -0,0 +1,318 @@
|
||||
# Design Token System - Empfohlene Features
|
||||
|
||||
## Übersicht
|
||||
|
||||
Dieses Dokument beschreibt empfohlene Erweiterungen für das Design Token System, die das System noch mächtiger und flexibler machen würden.
|
||||
|
||||
## Phase 1: Datenbank-Speicherung (Priorität: Hoch)
|
||||
|
||||
### 1.1 Database Schema
|
||||
- **Datei**: `src/Framework/Design/Migrations/CreateDesignTokensTable.php`
|
||||
- **Schema**:
|
||||
- `id` (VARCHAR 255, Primary Key)
|
||||
- `name` (VARCHAR 255, NOT NULL)
|
||||
- `type` (VARCHAR 50) - 'color', 'spacing', 'typography', etc.
|
||||
- `state` (VARCHAR 50, DEFAULT 'default')
|
||||
- `scope` (VARCHAR 100) - 'framework', 'admin', etc.
|
||||
- `value_data` (JSON) - Token-Wert
|
||||
- `base_token_id` (VARCHAR 255, NULL) - Für abgeleitete Tokens
|
||||
- `mutation_data` (JSON, NULL) - StateMutation als JSON
|
||||
- `description` (TEXT)
|
||||
- `metadata` (JSON)
|
||||
- `created_at`, `updated_at` (TIMESTAMP)
|
||||
- Indizes: `(name, state, scope)`, `scope`, `type`, `state`
|
||||
|
||||
### 1.2 DesignTokenEntity
|
||||
- **Datei**: `src/Framework/Design/Database/DesignTokenEntity.php`
|
||||
- **Funktionalität**: Entity-Mapping für Design Tokens
|
||||
- **Features**:
|
||||
- Serialisierung/Deserialisierung von Token-ValueObjects
|
||||
- StateMutation als JSON speichern/laden
|
||||
- Versionierung von Tokens
|
||||
|
||||
### 1.3 DatabaseTokenRepository
|
||||
- **Datei**: `src/Framework/Design/Repositories/DatabaseTokenRepository.php`
|
||||
- **Funktionalität**:
|
||||
- Lädt Tokens aus Datenbank
|
||||
- Konvertiert DB-Entities zu TokenInterface
|
||||
- Caching für Performance
|
||||
- Filterung nach Scope, Type, State
|
||||
|
||||
### 1.4 HybridTokenRegistry
|
||||
- **Datei**: `src/Framework/Design/ValueObjects/HybridTokenRegistry.php`
|
||||
- **Funktionalität**:
|
||||
- Lädt Code-basierte Tokens (wie bisher)
|
||||
- Lädt DB-Tokens (optional)
|
||||
- Merged beide Quellen (DB überschreibt Code)
|
||||
- Cache für Performance
|
||||
- Hot-Reload für DB-Änderungen (optional)
|
||||
|
||||
**Vorteile**:
|
||||
- Dynamische Anpassung ohne Code-Deployment
|
||||
- UI für Token-Verwaltung möglich
|
||||
- Versionierung/Audit-Trail
|
||||
- Multi-Tenant Support
|
||||
|
||||
## Phase 2: Token-Validierung & Linting (Priorität: Mittel)
|
||||
|
||||
### 2.1 Token Validator
|
||||
- **Datei**: `src/Framework/Design/Validators/TokenValidator.php`
|
||||
- **Funktionalität**:
|
||||
- Validiert Token-Werte (z.B. OKLCH-Werte im gültigen Bereich)
|
||||
- Prüft auf fehlende States
|
||||
- Prüft auf inkonsistente Mutationen
|
||||
- WCAG-Kontrast-Validierung für Farben
|
||||
|
||||
### 2.2 Token Linter
|
||||
- **Datei**: `src/Framework/Design/Linters/TokenLinter.php`
|
||||
- **Funktionalität**:
|
||||
- Prüft Naming Conventions
|
||||
- Prüft auf ungenutzte Tokens
|
||||
- Prüft auf fehlende Fallbacks
|
||||
- Prüft auf Performance-Probleme
|
||||
|
||||
### 2.3 Console Command
|
||||
- **Datei**: `src/Application/Console/Commands/Design/ValidateTokensCommand.php`
|
||||
- **Command**: `design:validate-tokens`
|
||||
- **Funktionalität**:
|
||||
- Validiert alle Tokens
|
||||
- Zeigt Linter-Warnungen
|
||||
- Prüft WCAG-Kontrast
|
||||
- Generiert Report
|
||||
|
||||
## Phase 3: Token-Export & Import (Priorität: Mittel)
|
||||
|
||||
### 3.1 Export-Formate
|
||||
- **JSON**: Standard JSON-Format für Tokens
|
||||
- **W3C Design Tokens**: W3C Design Tokens Format
|
||||
- **Style Dictionary**: Style Dictionary Format
|
||||
- **CSS Variables**: Reine CSS Custom Properties
|
||||
- **TypeScript**: TypeScript-Typen für Frontend
|
||||
|
||||
### 3.2 Import-Formate
|
||||
- **JSON**: Import aus JSON
|
||||
- **W3C Design Tokens**: Import aus W3C Format
|
||||
- **CSS**: Parse bestehende CSS Custom Properties
|
||||
- **Figma Tokens**: Import aus Figma Tokens Plugin
|
||||
|
||||
### 3.3 Console Commands
|
||||
- **Datei**: `src/Application/Console/Commands/Design/ExportTokensCommand.php`
|
||||
- **Command**: `design:export-tokens --format=json|w3c|css|typescript`
|
||||
- **Datei**: `src/Application/Console/Commands/Design/ImportTokensCommand.php`
|
||||
- **Command**: `design:import-tokens --file=path/to/tokens.json --format=json`
|
||||
|
||||
## Phase 4: Token-Themes & Varianten (Priorität: Hoch)
|
||||
|
||||
### 4.1 Theme System
|
||||
- **Datei**: `src/Framework/Design/ValueObjects/Theme.php`
|
||||
- **Funktionalität**:
|
||||
- Definiert Theme-Varianten (z.B. "Light", "Dark", "High Contrast")
|
||||
- Theme-spezifische Token-Überschreibungen
|
||||
- Theme-Wechsel zur Laufzeit
|
||||
|
||||
### 4.2 Theme Registry
|
||||
- **Datei**: `src/Framework/Design/ValueObjects/ThemeRegistry.php`
|
||||
- **Funktionalität**:
|
||||
- Verwaltet mehrere Themes
|
||||
- Theme-Aktivierung
|
||||
- Theme-Merging (Basis-Theme + Varianten)
|
||||
|
||||
### 4.3 CSS-Generierung für Themes
|
||||
- **Erweiterung**: `CssAstBuilder` generiert CSS für alle Themes
|
||||
- **Output**: Separate CSS-Dateien oder CSS-Variablen mit Theme-Präfix
|
||||
|
||||
**Beispiel**:
|
||||
```css
|
||||
[data-theme="light"] {
|
||||
--color-primary: oklch(70% 0.2 295);
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--color-primary: oklch(60% 0.22 295);
|
||||
}
|
||||
|
||||
[data-theme="high-contrast"] {
|
||||
--color-primary: oklch(50% 0.3 295);
|
||||
}
|
||||
```
|
||||
|
||||
## Phase 5: Token-Dokumentation & Storybook-Integration (Priorität: Niedrig)
|
||||
|
||||
### 5.1 Automatische Dokumentation
|
||||
- **Datei**: `src/Framework/Design/Documentation/TokenDocumentationGenerator.php`
|
||||
- **Funktionalität**:
|
||||
- Generiert Markdown-Dokumentation aus Tokens
|
||||
- Zeigt alle Token-Werte
|
||||
- Zeigt State-Variationen
|
||||
- Zeigt Verwendungsbeispiele
|
||||
|
||||
### 5.2 Storybook Integration
|
||||
- **Datei**: `src/Framework/Design/Storybook/TokenStoriesGenerator.php`
|
||||
- **Funktionalität**:
|
||||
- Generiert Storybook-Stories für Tokens
|
||||
- Visualisiert Token-Werte
|
||||
- Zeigt State-Variationen
|
||||
- Interaktive Token-Exploration
|
||||
|
||||
### 5.3 Token-Showcase
|
||||
- **Datei**: `src/Application/Admin/Controllers/DesignTokenShowcaseController.php`
|
||||
- **Funktionalität**:
|
||||
- Admin-UI für Token-Visualisierung
|
||||
- Interaktive Token-Exploration
|
||||
- State-Variationen anzeigen
|
||||
- Export-Funktionen
|
||||
|
||||
## Phase 6: Token-Performance-Optimierung (Priorität: Mittel)
|
||||
|
||||
### 6.1 CSS-Optimierung
|
||||
- **Minification**: Minimiert generiertes CSS
|
||||
- **Tree-Shaking**: Entfernt ungenutzte Tokens
|
||||
- **Critical CSS**: Extrahiert kritische Tokens
|
||||
- **CSS-Layering**: Optimiert @layer-Struktur
|
||||
|
||||
### 6.2 Caching-Strategien
|
||||
- **Token-Cache**: Cached geladene Tokens
|
||||
- **CSS-Cache**: Cached generiertes CSS
|
||||
- **Invalidation**: Intelligente Cache-Invalidierung
|
||||
- **Preloading**: Preload häufig verwendeter Tokens
|
||||
|
||||
### 6.3 Lazy Loading
|
||||
- **On-Demand Loading**: Lädt Tokens nur bei Bedarf
|
||||
- **Code-Splitting**: Splittet Token-CSS nach Scope
|
||||
- **Dynamic Imports**: Dynamische CSS-Imports
|
||||
|
||||
## Phase 7: Token-Testing & QA (Priorität: Mittel)
|
||||
|
||||
### 7.1 Unit Tests
|
||||
- **Token-Tests**: Testet Token-Erstellung und -Manipulation
|
||||
- **Mutation-Tests**: Testet State-Mutationen
|
||||
- **CSS-Generierung-Tests**: Testet CSS-Output
|
||||
|
||||
### 7.2 Visual Regression Tests
|
||||
- **Screenshot-Tests**: Vergleicht Token-Rendering
|
||||
- **State-Variation-Tests**: Testet alle States
|
||||
- **Theme-Tests**: Testet Theme-Varianten
|
||||
|
||||
### 7.3 Accessibility Tests
|
||||
- **Kontrast-Tests**: Automatische WCAG-Kontrast-Prüfung
|
||||
- **Color-Blindness-Tests**: Simuliert Farbenblindheit
|
||||
- **Screen-Reader-Tests**: Prüft Screen-Reader-Kompatibilität
|
||||
|
||||
## Phase 8: Token-Editor UI (Priorität: Niedrig)
|
||||
|
||||
### 8.1 Admin-Interface
|
||||
- **Token-Editor**: Visueller Editor für Tokens
|
||||
- **Color-Picker**: OKLCH-basierter Color-Picker
|
||||
- **State-Editor**: Editor für State-Mutationen
|
||||
- **Live-Preview**: Live-Vorschau der Änderungen
|
||||
|
||||
### 8.2 Token-Management
|
||||
- **CRUD-Operationen**: Create, Read, Update, Delete für Tokens
|
||||
- **Bulk-Operations**: Massenbearbeitung von Tokens
|
||||
- **Import/Export**: UI für Import/Export
|
||||
- **Versionierung**: UI für Token-Versionen
|
||||
|
||||
### 8.3 Analytics
|
||||
- **Token-Usage**: Zeigt Verwendung von Tokens
|
||||
- **Performance-Metriken**: Zeigt Performance-Impact
|
||||
- **Accessibility-Scores**: Zeigt Accessibility-Scores
|
||||
|
||||
## Phase 9: Erweiterte Farb-Features (Priorität: Niedrig)
|
||||
|
||||
### 9.1 Farb-Paletten-Generierung
|
||||
- **Datei**: `src/Framework/Design/Services/ColorPaletteGenerator.php`
|
||||
- **Funktionalität**:
|
||||
- Generiert Farb-Paletten aus Basis-Farbe
|
||||
- Harmonische Farb-Kombinationen
|
||||
- Kontrast-optimierte Paletten
|
||||
- Accessibility-optimierte Paletten
|
||||
|
||||
### 9.2 Farb-Analyse
|
||||
- **Datei**: `src/Framework/Design/Services/ColorAnalyzer.php`
|
||||
- **Funktionalität**:
|
||||
- Analysiert Farb-Eigenschaften
|
||||
- Berechnet Kontrast-Verhältnisse
|
||||
- Erkennt Farb-Harmonien
|
||||
- Simuliert Farbenblindheit
|
||||
|
||||
### 9.3 Farb-Konvertierung
|
||||
- **Erweiterung**: Unterstützung für weitere Farb-Räume
|
||||
- LAB, LUV, XYZ
|
||||
- CMYK
|
||||
- Pantone (näherungsweise)
|
||||
- NCS (Natural Color System)
|
||||
|
||||
## Phase 10: Token-Versionierung & Migration (Priorität: Mittel)
|
||||
|
||||
### 10.1 Token-Versionierung
|
||||
- **Datei**: `src/Framework/Design/ValueObjects/TokenVersion.php`
|
||||
- **Funktionalität**:
|
||||
- Versions-Tracking für Tokens
|
||||
- Semantische Versionierung
|
||||
- Changelog-Generierung
|
||||
- Breaking Changes Detection
|
||||
|
||||
### 10.2 Migration-Tools
|
||||
- **Datei**: `src/Framework/Design/Migrations/TokenMigration.php`
|
||||
- **Funktionalität**:
|
||||
- Automatische Token-Migrationen
|
||||
- Bulk-Updates
|
||||
- Deprecation-Warnings
|
||||
- Auto-Fix für einfache Änderungen
|
||||
|
||||
### 10.3 Rollback-Mechanismus
|
||||
- **Funktionalität**:
|
||||
- Rollback zu vorherigen Token-Versionen
|
||||
- Version-Vergleich
|
||||
- Diff-Visualisierung
|
||||
|
||||
## Priorisierung
|
||||
|
||||
### Kurzfristig (1-2 Monate)
|
||||
1. **Datenbank-Speicherung** (Phase 1) - Ermöglicht dynamische Token-Verwaltung
|
||||
2. **Token-Themes** (Phase 4) - Wichtiges Feature für Multi-Theme-Support
|
||||
3. **Token-Validierung** (Phase 2) - Qualitätssicherung
|
||||
|
||||
### Mittelfristig (3-6 Monate)
|
||||
4. **Token-Export/Import** (Phase 3) - Integration mit Design-Tools
|
||||
5. **Performance-Optimierung** (Phase 6) - Skalierbarkeit
|
||||
6. **Token-Testing** (Phase 7) - Qualitätssicherung
|
||||
|
||||
### Langfristig (6+ Monate)
|
||||
7. **Token-Dokumentation** (Phase 5) - Developer Experience
|
||||
8. **Token-Editor UI** (Phase 8) - Benutzerfreundlichkeit
|
||||
9. **Erweiterte Farb-Features** (Phase 9) - Erweiterte Funktionalität
|
||||
10. **Token-Versionierung** (Phase 10) - Enterprise-Features
|
||||
|
||||
## Abhängigkeiten
|
||||
|
||||
- **Phase 1** (Datenbank) ist Voraussetzung für **Phase 8** (Editor UI)
|
||||
- **Phase 2** (Validierung) ist Voraussetzung für **Phase 10** (Versionierung)
|
||||
- **Phase 4** (Themes) kann parallel zu anderen Phasen entwickelt werden
|
||||
- **Phase 6** (Performance) profitiert von allen vorherigen Phasen
|
||||
|
||||
## Technische Überlegungen
|
||||
|
||||
### Datenbank-Speicherung
|
||||
- **Hybrid-Ansatz**: Code-basierte Tokens als Default, DB als Override
|
||||
- **Caching**: Aggressives Caching für Performance
|
||||
- **Migration**: Schrittweise Migration von Code zu DB
|
||||
|
||||
### Performance
|
||||
- **Lazy Loading**: Tokens nur bei Bedarf laden
|
||||
- **CSS-Optimierung**: Minimierung und Tree-Shaking
|
||||
- **CDN-Integration**: Generiertes CSS auf CDN ausliefern
|
||||
|
||||
### Skalierbarkeit
|
||||
- **Multi-Tenant**: Unterstützung für mehrere Tenant-Token-Sets
|
||||
- **Caching-Strategien**: Verschiedene Caching-Ebenen
|
||||
- **Load Balancing**: Verteilung der Token-Generierung
|
||||
|
||||
## Erfolgs-Metriken
|
||||
|
||||
- **Developer Experience**: Reduzierte Zeit für Token-Verwaltung
|
||||
- **Performance**: Schnellere CSS-Generierung
|
||||
- **Qualität**: Weniger Token-Fehler durch Validierung
|
||||
- **Flexibilität**: Einfacheres Theme-Management
|
||||
- **Integration**: Bessere Integration mit Design-Tools
|
||||
|
||||
585
docs/todo/todo.md
Normal file
585
docs/todo/todo.md
Normal file
@@ -0,0 +1,585 @@
|
||||
# Console Template System & ConsoleResponse - Implementation Plan
|
||||
|
||||
## 📋 Übersicht
|
||||
|
||||
Implementierung eines Template-Systems für Console-Ausgaben mit `ConsoleResponse` Value Objects, das nahtlos mit dem bestehenden attribute-basierten Command-System arbeitet.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Phase 1: ConsoleResponse Foundation (Woche 1)
|
||||
|
||||
### 1.1 Core Interfaces & Base Classes
|
||||
|
||||
**Ziel**: Framework-compliant Response-Architektur
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleResponse` Interface erstellen
|
||||
- `getExitCode(): ExitCode`
|
||||
- `render(ConsoleOutputInterface $output): void`
|
||||
- `getData(): array`
|
||||
|
||||
- [ ] `AbstractConsoleResponse` Base Class
|
||||
- Readonly class mit gemeinsamer Logik
|
||||
- Constructor mit ExitCode und Data
|
||||
- Gemeinsame Methoden-Implementierung
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Response/ConsoleResponse.php`
|
||||
- `src/Framework/Console/Response/AbstractConsoleResponse.php`
|
||||
|
||||
### 1.2 Response Type Implementations
|
||||
|
||||
**Ziel**: 6 konkrete Response-Typen für verschiedene Use Cases
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleTextResponse` - Simple Text-Ausgabe
|
||||
- Factory Methods: `success()`, `error()`, `warning()`, `info()`
|
||||
- Support für ConsoleStyle
|
||||
- Readonly Value Object
|
||||
|
||||
- [ ] `ConsoleTableResponse` - Table-basierte Ausgabe
|
||||
- Wiederverwendung der bestehenden `Table` Klasse
|
||||
- Optional: Title Support
|
||||
- Headers und Rows als Constructor-Parameter
|
||||
|
||||
- [ ] `ConsoleTemplateResponse` - Template-basierte Ausgabe
|
||||
- Template Name + Data
|
||||
- Integration mit ConsoleTemplateEngine (Phase 2)
|
||||
- Lazy Loading von Template
|
||||
|
||||
- [ ] `ConsoleLayoutResponse` - Component-basierte Ausgabe
|
||||
- ConsoleLayout als Parameter
|
||||
- Wiederverwendung von Layout-System
|
||||
- Multi-Component Rendering
|
||||
|
||||
- [ ] `ConsoleMenuResponse` - Interactive Menu
|
||||
- Wiederverwendung von `InteractiveMenu`
|
||||
- Items, Title, Interactive-Flag
|
||||
- Selected Value in Data speichern
|
||||
|
||||
- [ ] `ConsoleCompositeResponse` - Mehrere Responses kombinieren
|
||||
- Array von ConsoleResponse
|
||||
- Sequential Rendering
|
||||
- Exit Code Aggregation (schlechtester gewinnt)
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Response/ConsoleTextResponse.php`
|
||||
- `src/Framework/Console/Response/ConsoleTableResponse.php`
|
||||
- `src/Framework/Console/Response/ConsoleTemplateResponse.php`
|
||||
- `src/Framework/Console/Response/ConsoleLayoutResponse.php`
|
||||
- `src/Framework/Console/Response/ConsoleMenuResponse.php`
|
||||
- `src/Framework/Console/Response/ConsoleCompositeResponse.php`
|
||||
|
||||
### 1.3 Response Builder (Fluent API)
|
||||
|
||||
**Ziel**: Einfache Erstellung komplexer Responses
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleResponseBuilder` implementieren
|
||||
- `addText(string, ?ConsoleStyle): self`
|
||||
- `addTable(array, array, ?string): self`
|
||||
- `addTemplate(string, array): self`
|
||||
- `addDivider(): self`
|
||||
- `addNewLine(int): self`
|
||||
- `withExitCode(ExitCode): self`
|
||||
- `withData(array): self`
|
||||
- `build(): ConsoleResponse`
|
||||
|
||||
**Features**:
|
||||
- Fluent Interface
|
||||
- Auto-Composite wenn mehrere Sections
|
||||
- Single Response wenn nur eine Section
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Response/ConsoleResponseBuilder.php`
|
||||
|
||||
### 1.4 Console Application Integration
|
||||
|
||||
**Ziel**: Support für ExitCode UND ConsoleResponse Return-Typen
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleApplication` erweitern für beide Return-Typen
|
||||
- Pattern Matching für `ExitCode|ConsoleResponse|int`
|
||||
- `handleResponse(ConsoleResponse): int` Methode
|
||||
- Backward Compatibility sicherstellen
|
||||
|
||||
- [ ] Method Signature Patterns dokumentieren
|
||||
- Pattern A: `ConsoleResponse` only (Recommended)
|
||||
- Pattern B: `ExitCode` only (Legacy)
|
||||
- Pattern C: Both (Transition)
|
||||
- Pattern D: Union Type (Flexible)
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/ConsoleApplication.php` (Update)
|
||||
|
||||
### 1.5 Tests Phase 1
|
||||
|
||||
**Ziel**: ≥80% Test Coverage für Response-System
|
||||
|
||||
**Tasks**:
|
||||
- [ ] Unit Tests für alle Response-Typen
|
||||
- Constructor-Validierung
|
||||
- Rendering-Logik
|
||||
- Factory Methods
|
||||
- getData() und getExitCode()
|
||||
|
||||
- [ ] Integration Tests
|
||||
- ConsoleApplication mit verschiedenen Return-Typen
|
||||
- ConsoleResponseBuilder Workflows
|
||||
- Composite Response mit mehreren Sections
|
||||
|
||||
- [ ] Example Commands
|
||||
- Beispiel für jede Response-Type
|
||||
- Migration-Guide-Examples
|
||||
|
||||
**Files**:
|
||||
- `tests/Unit/Console/Response/*Test.php`
|
||||
- `tests/Feature/Console/ResponseIntegrationTest.php`
|
||||
|
||||
**Deliverables Phase 1**:
|
||||
- ✅ ConsoleResponse Interface & 6 Typen
|
||||
- ✅ ConsoleResponseBuilder Fluent API
|
||||
- ✅ Backward-Compatible Console Application
|
||||
- ✅ Comprehensive Test Suite
|
||||
- ✅ Documentation & Examples
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Phase 2: Console Template System (Woche 2)
|
||||
|
||||
### 2.1 String-Based Template Processors
|
||||
|
||||
**Ziel**: Template-Verarbeitung ähnlich wie HTML-Template-System
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleTemplateProcessor` Interface
|
||||
- `process(string $template, array $data): string`
|
||||
- String-basiert (nicht DOM!)
|
||||
|
||||
- [ ] `ConsolePlaceholderProcessor`
|
||||
- `{{ variable }}` - Escaped Output
|
||||
- `{{{ raw }}}` - Raw Output (für ANSI codes)
|
||||
- `{{ object.property }}` - Dot-Notation
|
||||
- `{{ function() }}` - Function Calls
|
||||
- Ähnlich wie `PlaceholderReplacer` aus View-System
|
||||
|
||||
- [ ] `ConsoleForProcessor`
|
||||
- `<for items="..." as="...">` Syntax
|
||||
- String-basiertes Loop-Processing
|
||||
- Nested Loop Support
|
||||
- Dot-Notation für Items-Path
|
||||
|
||||
- [ ] `ConsoleIfProcessor` (Optional)
|
||||
- `<if condition="...">` Syntax
|
||||
- Simple Conditional Logic
|
||||
- `<else>` Support
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Template/ConsoleTemplateProcessor.php`
|
||||
- `src/Framework/Console/Template/Processors/ConsolePlaceholderProcessor.php`
|
||||
- `src/Framework/Console/Template/Processors/ConsoleForProcessor.php`
|
||||
- `src/Framework/Console/Template/Processors/ConsoleIfProcessor.php`
|
||||
|
||||
### 2.2 Template Engine & Loader
|
||||
|
||||
**Ziel**: Unified Template Processing Pipeline
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleTemplateEngine` implementieren
|
||||
- Processor Pipeline (ähnlich wie View\Engine)
|
||||
- Sequential Processing
|
||||
- Processor Registry
|
||||
- Error Handling
|
||||
|
||||
- [ ] `ConsoleTemplateLoader`
|
||||
- Template-Files laden aus `/templates/console/`
|
||||
- File Extension: `.console.txt` oder `.console.tpl`
|
||||
- Template Caching (optional)
|
||||
- Template Not Found Exception
|
||||
|
||||
- [ ] Template Syntax Documentation
|
||||
- Placeholder Syntax
|
||||
- Component Tags
|
||||
- Loop Syntax
|
||||
- Examples
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Template/ConsoleTemplateEngine.php`
|
||||
- `src/Framework/Console/Template/Loading/ConsoleTemplateLoader.php`
|
||||
- `docs/console-template-syntax.md`
|
||||
|
||||
### 2.3 Console Component Tags
|
||||
|
||||
**Ziel**: `<console:table>`, `<console:box>` etc. in Templates
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleComponentTagProcessor` implementieren
|
||||
- Parse `<console:*>` Tags
|
||||
- Extract Attributes
|
||||
- Extract Content
|
||||
- Render via Component Registry
|
||||
|
||||
- [ ] `ConsoleComponentRegistry`
|
||||
- Registry für Component-Mapping
|
||||
- `table` → TableAdapter
|
||||
- `box` → TextBoxAdapter
|
||||
- `menu` → MenuAdapter
|
||||
- `progress` → ProgressBarAdapter
|
||||
|
||||
- [ ] Console Component Adapters (Template-friendly)
|
||||
- `ConsoleTableComponent`
|
||||
- `ConsoleBoxComponent`
|
||||
- `ConsoleMenuComponent`
|
||||
- `ConsoleProgressComponent`
|
||||
|
||||
**Features**:
|
||||
- Template Tag: `<console:table headers="A,B,C">...</console:table>`
|
||||
- Nested Content Processing (für Loops in Attributen)
|
||||
- Attribute Parsing
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Template/Processors/ConsoleComponentTagProcessor.php`
|
||||
- `src/Framework/Console/Template/Components/ConsoleComponentRegistry.php`
|
||||
- `src/Framework/Console/Template/Components/ConsoleTableComponent.php`
|
||||
- `src/Framework/Console/Template/Components/ConsoleBoxComponent.php`
|
||||
|
||||
### 2.4 Template Response Integration
|
||||
|
||||
**Ziel**: ConsoleTemplateResponse nutzt Template-System
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleTemplateResponse` finalisieren
|
||||
- Integration mit ConsoleTemplateEngine
|
||||
- Template Loading
|
||||
- Data Binding
|
||||
- Error Handling
|
||||
|
||||
- [ ] Template Examples erstellen
|
||||
- `migration-status.console.txt`
|
||||
- `user-list.console.txt`
|
||||
- `health-check.console.txt`
|
||||
- `system-status.console.txt`
|
||||
|
||||
**Files**:
|
||||
- `templates/console/migration-status.console.txt`
|
||||
- `templates/console/user-list.console.txt`
|
||||
- `templates/console/health-check.console.txt`
|
||||
|
||||
### 2.5 Tests Phase 2
|
||||
|
||||
**Ziel**: Template-System vollständig getestet
|
||||
|
||||
**Tasks**:
|
||||
- [ ] Processor Tests
|
||||
- PlaceholderProcessor mit verschiedenen Syntaxen
|
||||
- ForProcessor mit Nested Loops
|
||||
- ComponentTagProcessor mit allen Tags
|
||||
|
||||
- [ ] Integration Tests
|
||||
- ConsoleTemplateEngine mit kompletten Templates
|
||||
- ConsoleTemplateResponse Rendering
|
||||
- Error Cases (Template Not Found, Parse Errors)
|
||||
|
||||
- [ ] Template Examples Tests
|
||||
- Alle Template-Files rendern korrekt
|
||||
- Data-Binding funktioniert
|
||||
- Component Tags werden ersetzt
|
||||
|
||||
**Files**:
|
||||
- `tests/Unit/Console/Template/*Test.php`
|
||||
- `tests/Feature/Console/TemplateRenderingTest.php`
|
||||
|
||||
**Deliverables Phase 2**:
|
||||
- ✅ String-based Template Processing System
|
||||
- ✅ ConsoleTemplateEngine mit Processor Pipeline
|
||||
- ✅ Console Component Tags Support
|
||||
- ✅ Template Loader mit Caching
|
||||
- ✅ Template Examples & Documentation
|
||||
- ✅ Comprehensive Tests
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Phase 3: Component-Based Layout System (Woche 3) - OPTIONAL
|
||||
|
||||
### 3.1 ConsoleComponent Interface & Adapters
|
||||
|
||||
**Ziel**: Unified Interface für bestehende Console-Components
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleComponent` Interface
|
||||
- `render(ConsoleOutputInterface $output): void`
|
||||
- Standard-Interface für alle Components
|
||||
|
||||
- [ ] Adapters für bestehende Components
|
||||
- `TableAdapter` - Wrapper für mutable `Table`
|
||||
- `TextBoxAdapter` - Wrapper für readonly `TextBox`
|
||||
- `MenuAdapter` - Wrapper für `InteractiveMenu`
|
||||
- `ProgressBarAdapter` - Wrapper für `ProgressBar`
|
||||
- `SpinnerAdapter` - Wrapper für `Spinner`
|
||||
|
||||
**Benefits**:
|
||||
- Readonly Adapters trotz mutable Original-Components
|
||||
- Unified Interface für Layout-System
|
||||
- Backward-Compatible
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Components/ConsoleComponent.php`
|
||||
- `src/Framework/Console/Components/Adapters/TableAdapter.php`
|
||||
- `src/Framework/Console/Components/Adapters/TextBoxAdapter.php`
|
||||
- `src/Framework/Console/Components/Adapters/MenuAdapter.php`
|
||||
- `src/Framework/Console/Components/Adapters/ProgressBarAdapter.php`
|
||||
- `src/Framework/Console/Components/Adapters/SpinnerAdapter.php`
|
||||
|
||||
### 3.2 Layout & Section Value Objects
|
||||
|
||||
**Ziel**: Komposition von Components zu Layouts
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleLayout` Value Object
|
||||
- Array von ConsoleSections
|
||||
- Sequential Rendering
|
||||
- Readonly Class
|
||||
|
||||
- [ ] `ConsoleSection` Value Object
|
||||
- ConsoleComponent + Style
|
||||
- Margins, Padding, Borders
|
||||
- Readonly Class
|
||||
|
||||
- [ ] `ConsoleSectionStyle` Value Object
|
||||
- Margins (top, bottom)
|
||||
- Padding
|
||||
- Borders (top, bottom, divider)
|
||||
- Background Colors (optional)
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Layout/ConsoleLayout.php`
|
||||
- `src/Framework/Console/Layout/ConsoleSection.php`
|
||||
- `src/Framework/Console/Layout/ConsoleSectionStyle.php`
|
||||
|
||||
### 3.3 Layout Builder (Fluent API)
|
||||
|
||||
**Ziel**: Einfache Layout-Erstellung
|
||||
|
||||
**Tasks**:
|
||||
- [ ] `ConsoleLayoutBuilder`
|
||||
- `addSection(ConsoleComponent): self`
|
||||
- `addTable(array, array): self`
|
||||
- `addBox(string, ?string): self`
|
||||
- `addDivider(): self`
|
||||
- `addSpacing(int): self`
|
||||
- `build(): ConsoleLayout`
|
||||
|
||||
**Files**:
|
||||
- `src/Framework/Console/Layout/ConsoleLayoutBuilder.php`
|
||||
|
||||
### 3.4 Tests Phase 3
|
||||
|
||||
**Tasks**:
|
||||
- [ ] Component Adapter Tests
|
||||
- [ ] Layout Composition Tests
|
||||
- [ ] Builder Tests
|
||||
- [ ] Integration Tests
|
||||
|
||||
**Deliverables Phase 3**:
|
||||
- ✅ ConsoleComponent Interface & 5 Adapters
|
||||
- ✅ ConsoleLayout System
|
||||
- ✅ ConsoleLayoutBuilder Fluent API
|
||||
- ✅ Tests & Documentation
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation & Examples
|
||||
|
||||
### Documentation Tasks
|
||||
- [ ] Console Response Guide
|
||||
- Alle Response-Typen mit Beispielen
|
||||
- Migration Guide (ExitCode → ConsoleResponse)
|
||||
- Best Practices
|
||||
|
||||
- [ ] Console Template Guide
|
||||
- Template Syntax Reference
|
||||
- Component Tags Reference
|
||||
- Template Examples
|
||||
|
||||
- [ ] Console Layout Guide
|
||||
- Component-Based Layouts
|
||||
- Builder Pattern Examples
|
||||
- Advanced Compositions
|
||||
|
||||
### Example Commands
|
||||
- [ ] Simple Response Examples
|
||||
- TextResponse in verschiedenen Commands
|
||||
- TableResponse für Listen
|
||||
- TemplateResponse für komplexe Ausgaben
|
||||
|
||||
- [ ] Template Examples
|
||||
- Migration Status Template
|
||||
- User List Template
|
||||
- Health Check Template
|
||||
- System Status Template
|
||||
|
||||
- [ ] Layout Examples
|
||||
- Dashboard Layout
|
||||
- Report Layout
|
||||
- Multi-Section Layout
|
||||
|
||||
**Files**:
|
||||
- `docs/console/console-response-guide.md`
|
||||
- `docs/console/console-template-guide.md`
|
||||
- `docs/console/console-layout-guide.md`
|
||||
- `src/Framework/Console/Examples/*` (Update)
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Strategy
|
||||
|
||||
### Unit Tests
|
||||
- [ ] Alle Response-Typen
|
||||
- [ ] Alle Processors
|
||||
- [ ] Alle Adapters
|
||||
- [ ] Builder Classes
|
||||
|
||||
### Integration Tests
|
||||
- [ ] ConsoleApplication mit verschiedenen Return-Typen
|
||||
- [ ] Template Rendering End-to-End
|
||||
- [ ] Layout Composition
|
||||
- [ ] Backward Compatibility
|
||||
|
||||
### Example Tests
|
||||
- [ ] Alle Example-Commands ausführbar
|
||||
- [ ] Template-Examples rendern korrekt
|
||||
- [ ] Layout-Examples funktionieren
|
||||
|
||||
**Coverage Goal**: ≥80%
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Migration Strategy
|
||||
|
||||
### Backward Compatibility
|
||||
- ✅ Alle bestehenden Commands funktionieren unverändert
|
||||
- ✅ ExitCode Return-Type weiterhin supported
|
||||
- ✅ Direktes Output-Schreiben weiterhin möglich
|
||||
|
||||
### Gradual Adoption
|
||||
1. **Phase 1**: ConsoleResponse verfügbar, optional nutzbar
|
||||
2. **Phase 2**: Template-System verfügbar, Commands können migrieren
|
||||
3. **Phase 3**: Layout-System verfügbar für komplexe Use Cases
|
||||
|
||||
### Migration Patterns
|
||||
```php
|
||||
// Old: ExitCode + Direct Output
|
||||
#[ConsoleCommand(name: 'old')]
|
||||
public function old(ConsoleInput $input, ConsoleOutput $output): ExitCode
|
||||
{
|
||||
$output->writeLine("Done");
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
|
||||
// New: ConsoleResponse
|
||||
#[ConsoleCommand(name: 'new')]
|
||||
public function new(ConsoleInput $input): ConsoleResponse
|
||||
{
|
||||
return ConsoleTextResponse::success("Done");
|
||||
}
|
||||
|
||||
// Transition: Both
|
||||
#[ConsoleCommand(name: 'transition')]
|
||||
public function transition(ConsoleInput $input, ConsoleOutput $output): ConsoleResponse
|
||||
{
|
||||
$output->writeLine("Processing..."); // Progress output
|
||||
return ConsoleTextResponse::success("Done"); // Final result
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Success Criteria
|
||||
|
||||
### Phase 1 Complete When:
|
||||
- [ ] All 6 Response-Typen implementiert und getestet
|
||||
- [ ] ConsoleResponseBuilder funktional
|
||||
- [ ] ConsoleApplication unterstützt beide Return-Typen
|
||||
- [ ] ≥80% Test Coverage
|
||||
- [ ] Documentation vollständig
|
||||
|
||||
### Phase 2 Complete When:
|
||||
- [ ] Template-System funktional (Placeholder, For, Component Tags)
|
||||
- [ ] ConsoleTemplateResponse integriert
|
||||
- [ ] Template-Examples funktionieren
|
||||
- [ ] ≥80% Test Coverage
|
||||
- [ ] Documentation vollständig
|
||||
|
||||
### Phase 3 Complete When:
|
||||
- [ ] Component Adapters funktional
|
||||
- [ ] Layout-System implementiert
|
||||
- [ ] Builder Pattern verfügbar
|
||||
- [ ] ≥80% Test Coverage
|
||||
- [ ] Documentation vollständig
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Quick Start (Phase 1 only - Minimum Viable)
|
||||
|
||||
**Minimal Implementation für sofortige Nutzbarkeit**:
|
||||
|
||||
1. ConsoleResponse Interface
|
||||
2. ConsoleTextResponse (mit Factory Methods)
|
||||
3. ConsoleTableResponse
|
||||
4. ConsoleCompositeResponse
|
||||
5. ConsoleResponseBuilder
|
||||
6. ConsoleApplication Update
|
||||
|
||||
**Result**: Commands können sofort ConsoleResponse nutzen ohne Template-System!
|
||||
|
||||
---
|
||||
|
||||
## 📊 Timeline Estimate
|
||||
|
||||
- **Phase 1**: 1 Woche (Foundation - ESSENTIAL)
|
||||
- **Phase 2**: 1 Woche (Template System - RECOMMENDED)
|
||||
- **Phase 3**: 1 Woche (Layout System - OPTIONAL)
|
||||
|
||||
**Minimum Viable**: Phase 1 only (1 Woche)
|
||||
**Recommended**: Phase 1 + 2 (2 Wochen)
|
||||
**Complete**: Phase 1 + 2 + 3 (3 Wochen)
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related Systems
|
||||
|
||||
### Integration Points
|
||||
- **Console Module**: Bestehende Components (Table, TextBox, Menu, etc.)
|
||||
- **View System**: Template-Processor-Pattern wiederverwenden
|
||||
- **HTTP System**: Response-Pattern analog zu JsonResponse, ViewResult
|
||||
- **Testing**: Pest Framework für alle Tests
|
||||
|
||||
### Dependencies
|
||||
- Bestehende Console Components (keine Änderungen)
|
||||
- ConsoleOutput Interface (keine Änderungen)
|
||||
- ExitCode Enum (keine Änderungen)
|
||||
- ConsoleStyle, ConsoleColor (keine Änderungen)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Notes & Decisions
|
||||
|
||||
### Design Decisions
|
||||
1. **String-based Templates** (nicht DOM) - Einfacher für Console-Output
|
||||
2. **Method-based Commands** - Kompatibel mit `#[ConsoleCommand]` Attribute
|
||||
3. **Backward Compatible** - Beide Return-Typen (ExitCode | ConsoleResponse)
|
||||
4. **Adapter Pattern** - Wrapper für bestehende mutable Components
|
||||
5. **Composition** - Layout aus Components komponieren, nicht erben
|
||||
|
||||
### Open Questions
|
||||
- [ ] Template-Caching Strategy?
|
||||
- [ ] Performance-Optimierung für große Outputs?
|
||||
- [ ] ANSI-Code-Handling in Templates?
|
||||
- [ ] Interactive Components in Templates?
|
||||
|
||||
### Future Enhancements
|
||||
- Progress Bars in Templates
|
||||
- Animated Spinners in Templates
|
||||
- Color Themes für Templates
|
||||
- Template Inheritance/Extends
|
||||
- Custom Template Functions
|
||||
33
docs/troubleshooting/gitea-actions-artifact-compatibility.md
Normal file
33
docs/troubleshooting/gitea-actions-artifact-compatibility.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Gitea Actions Artifact Compatibility Issue
|
||||
|
||||
## Problem
|
||||
|
||||
Gitea Actions verwendet eine GHES (GitHub Enterprise Server) kompatible API. Die `@actions/artifact` Actions v2.0.0+ (insbesondere `upload-artifact@v4` und `download-artifact@v4`) werden auf GHES nicht unterstützt.
|
||||
|
||||
### Fehlermeldung
|
||||
|
||||
```
|
||||
GHESNotSupportedError: @actions/artifact v2.0.0+, upload-artifact@v4+ and download-artifact@v4+ are not currently supported on GHES.
|
||||
```
|
||||
|
||||
## Lösung
|
||||
|
||||
Verwende **v3** Versionen der Artifact Actions:
|
||||
|
||||
- `actions/upload-artifact@v3` (statt `@v4`)
|
||||
- `actions/download-artifact@v3` (statt `@v4`)
|
||||
|
||||
## Betroffene Workflows
|
||||
|
||||
- `.gitea/workflows/build-image.yml` - 12 Vorkommen
|
||||
- `.gitea/workflows/monitor-performance.yml` - 1 Vorkommen
|
||||
|
||||
## Migration
|
||||
|
||||
Alle `@v4` Versionen wurden auf `@v3` downgraded. Die v3 Versionen sind vollständig kompatibel mit Gitea Actions und bieten die gleiche Funktionalität.
|
||||
|
||||
## Referenzen
|
||||
|
||||
- [Gitea Actions Documentation](https://docs.gitea.com/usage/actions/overview)
|
||||
- [GitHub Actions Artifact v3 Documentation](https://github.com/actions/upload-artifact/tree/v3)
|
||||
|
||||
Reference in New Issue
Block a user