opticore-profiler 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/dist/assets/css/debug-message.css +16 -0
  4. package/dist/assets/css/home-page.css +177 -0
  5. package/dist/assets/css/profiler-detail.css +215 -0
  6. package/dist/assets/css/profiler-list.css +131 -0
  7. package/dist/assets/css/toolbar-standalone.css +213 -0
  8. package/dist/assets/js/home-particles.js +65 -0
  9. package/dist/assets/js/profiler-detail.js +77 -0
  10. package/dist/assets/js/profiler-list.js +76 -0
  11. package/dist/assets/js/toolbar.js +230 -0
  12. package/dist/index.cjs +1942 -0
  13. package/dist/index.d.cts +488 -0
  14. package/dist/index.d.ts +488 -0
  15. package/dist/index.js +1893 -0
  16. package/dist/utils/translations/message.translation.en.json +190 -0
  17. package/dist/utils/translations/message.translation.fr.json +190 -0
  18. package/dist/views/templates/home.njk +120 -0
  19. package/dist/views/templates/macros/icons.njk +19 -0
  20. package/dist/views/templates/macros/tables.njk +17 -0
  21. package/dist/views/templates/message.njk +14 -0
  22. package/dist/views/templates/panels/configuration.njk +16 -0
  23. package/dist/views/templates/panels/database.njk +36 -0
  24. package/dist/views/templates/panels/exception.njk +26 -0
  25. package/dist/views/templates/panels/logs.njk +44 -0
  26. package/dist/views/templates/panels/performance.njk +56 -0
  27. package/dist/views/templates/panels/request.njk +141 -0
  28. package/dist/views/templates/panels/routes.njk +51 -0
  29. package/dist/views/templates/panels/routing.njk +22 -0
  30. package/dist/views/templates/profiler-detail.njk +114 -0
  31. package/dist/views/templates/profiler-list.njk +163 -0
  32. package/dist/views/templates/toolbar-wdt.njk +161 -0
  33. package/package.json +73 -0
@@ -0,0 +1,190 @@
1
+ {
2
+ "common.none": "None",
3
+ "common.search": "Search",
4
+ "common.filter_placeholder": "Filter…",
5
+ "common.search_ellipsis": "search…",
6
+ "common.key": "Key",
7
+ "common.value": "Value",
8
+ "common.header": "Header",
9
+ "common.name": "Name",
10
+
11
+ "home.hero_sub": "API REST Server",
12
+ "home.hero_desc": "Your server is up and running. Start making requests to your API endpoints.",
13
+ "home.running_on_port": "Running on port {port}",
14
+ "home.host_label": "Host",
15
+ "home.host_sub": "API base URL",
16
+ "home.node_sub": "Runtime version",
17
+ "home.app_version_label": "App version",
18
+ "home.app_version_sub": "Template mysqldb",
19
+ "home.environment_label": "Environment",
20
+ "home.debug_toolbar_section": "Web Debug Toolbar",
21
+ "home.request_profiler_label": "Request Profiler",
22
+ "home.request_profiler_desc": "All captured requests",
23
+ "home.latest_request_label": "Latest Request",
24
+ "home.latest_request_desc": "Jump to the last profile",
25
+ "home.getting_started": "Getting Started",
26
+ "home.api_ready_title": "REST API is ready",
27
+ "home.api_ready_body_1": "This is a REST API server — no UI routes are defined by default.",
28
+ "home.api_ready_body_2": "Test your endpoints with {code} or use a client like Postman, Insomnia, or curl.",
29
+ "home.api_ready_body_3": "Each API response includes {token} and {tokenLink} headers pointing to the profiler.",
30
+
31
+ "toolbar.profile_token": "Profile token",
32
+ "toolbar.method": "Method",
33
+ "toolbar.url": "URL",
34
+ "toolbar.status": "Status",
35
+ "toolbar.duration": "Duration",
36
+ "toolbar.open_profiler": "Open Profiler",
37
+ "toolbar.total_time": "Total time",
38
+ "toolbar.application": "Application",
39
+ "toolbar.database": "Database",
40
+ "toolbar.performance": "Performance",
41
+ "toolbar.heap_used": "Heap used",
42
+ "toolbar.errors": "Errors",
43
+ "toolbar.warnings": "Warnings",
44
+ "toolbar.deprecations": "Deprecations",
45
+ "toolbar.logs": "Logs",
46
+ "toolbar.routing": "Routing",
47
+ "toolbar.http_requests": "HTTP requests",
48
+ "toolbar.last_n_requests": "Last {n} HTTP Requests",
49
+ "toolbar.view_all": "View all →",
50
+ "toolbar.no_requests_yet": "No requests yet.",
51
+ "toolbar.server": "Server",
52
+ "toolbar.nodejs": "Node.js",
53
+ "toolbar.environment": "Environment",
54
+ "toolbar.documentation": "📖 Documentation",
55
+ "toolbar.help_support": "❓ Help & Support",
56
+ "toolbar.open_profiler_emoji": "📊 Open Profiler",
57
+ "toolbar.profiler": "Profiler",
58
+ "toolbar.minimize": "Minimize toolbar",
59
+ "toolbar.expand": "Expand toolbar",
60
+
61
+ "list.search_placeholder": "search method, URL, status, token or IP",
62
+ "list.profile_search": "Profile Search",
63
+ "list.search_profiles": "Search profiles",
64
+ "list.latest": "Latest",
65
+ "list.ip": "IP",
66
+ "list.method": "Method",
67
+ "list.status": "Status",
68
+ "list.search_label": "Search",
69
+ "list.token": "Token",
70
+ "list.from": "From",
71
+ "list.until": "Until",
72
+ "list.results": "Results",
73
+ "list.search_btn": "Search",
74
+ "list.settings": "Profiler settings",
75
+ "list.http_requests_tab": "HTTP Requests",
76
+ "list.console_commands_tab": "Console Commands",
77
+ "list.no_commands": "No console commands have been executed yet.",
78
+ "list.results_found_one": "{n} result found",
79
+ "list.results_found_other": "{n} results found",
80
+ "list.col_status": "Status",
81
+ "list.col_method": "Method",
82
+ "list.col_url": "URL",
83
+ "list.col_time": "Time",
84
+ "list.col_token": "Token",
85
+ "list.no_requests_found": "No requests found. Try adjusting your filters.",
86
+ "list.method_any": "Any",
87
+
88
+ "message.not_found_title": "Profile Not Found",
89
+ "message.not_found_heading": "Profile not found",
90
+ "message.not_found_body": "Token <code>{token}</code> does not exist or has expired.",
91
+ "message.back_to_list": "← Back to profiler list",
92
+
93
+ "panel.request": "Request / Response",
94
+ "panel.performance": "Performance",
95
+ "panel.logs": "Log Messages",
96
+ "panel.routing": "Routing",
97
+ "panel.configuration": "Configuration",
98
+ "panel.database": "Database",
99
+ "panel.exception": "Exception",
100
+ "panel.routes": "Routes",
101
+
102
+ "detail.search_placeholder": "search on opticorejs.com",
103
+
104
+ "request.tab_request": "Request",
105
+ "request.tab_response": "Response",
106
+ "request.tab_cookies": "Cookies",
107
+ "request.tab_session": "Session",
108
+ "request.tab_server": "Server Parameters",
109
+ "request.get_params": "GET Parameters",
110
+ "request.post_params": "POST Parameters",
111
+ "request.uploaded_files": "Uploaded Files",
112
+ "request.route_params": "Route Parameters",
113
+ "request.attributes": "Request Attributes",
114
+ "request.headers": "Request Headers",
115
+ "request.content": "Request Content",
116
+ "request.no_body": "No request body.",
117
+ "request.retry_command": "Retry Command",
118
+ "request.copy_curl": "Copy as cURL",
119
+ "request.response_headers": "Response Headers",
120
+ "request.response_content": "Response Content",
121
+ "request.no_response_body": "No response body captured.",
122
+ "request.request_cookies": "Request Cookies",
123
+ "request.response_cookies": "Response Cookies",
124
+ "request.session_attributes": "Session Attributes",
125
+ "request.no_session": "No data related to this section has been collected. This application doesn't use sessions.",
126
+ "request.server_params": "Server Parameters",
127
+ "panel.http_status": "HTTP Status",
128
+ "panel.duration": "Duration",
129
+ "panel.memory": "Memory",
130
+ "panel.sql_queries": "SQL Queries",
131
+
132
+ "performance.title": "Performance metrics",
133
+ "performance.total_execution": "Total execution time",
134
+ "performance.app_time": "Application time",
135
+ "performance.peak_memory": "Peak memory usage",
136
+ "performance.execution_timeline": "Execution timeline",
137
+ "performance.threshold": "Threshold",
138
+ "performance.threshold_hint": "(timeline only displays events with a duration longer than this threshold)",
139
+ "performance.application_legend": "application",
140
+ "performance.database_legend": "database",
141
+ "performance.no_timing_events": "No timing events recorded for this request.",
142
+ "performance.process_info": "Process Info",
143
+
144
+ "logs.no_logs": "No log entries were recorded for this request.",
145
+ "logs.hint": "Use {code} to add entries.",
146
+ "logs.all_messages": "All messages",
147
+ "logs.errors": "Errors",
148
+ "logs.deprecations": "Deprecations",
149
+ "logs.warnings": "Warnings",
150
+ "logs.time": "Time",
151
+ "logs.message": "Message",
152
+ "logs.show_context": "Show context",
153
+ "logs.hide_context": "Hide context",
154
+ "logs.show_trace": "Show trace",
155
+
156
+ "database.no_queries": "No SQL queries were recorded for this request.",
157
+ "database.hint": "Use {code} in your repositories.",
158
+ "database.title": "Database Queries",
159
+ "database.total_queries": "Total Queries",
160
+ "database.total_time": "Total Time",
161
+ "database.avg_per_query": "Avg per Query",
162
+ "database.sql_queries": "SQL Queries",
163
+ "database.error_badge": "ERROR",
164
+ "database.bindings": "Bindings:",
165
+ "database.error_label": "Error:",
166
+
167
+ "exception.title": "Exceptions",
168
+ "exception.no_exceptions": "No exceptions were thrown during this request.",
169
+
170
+ "routes.title": "Routes",
171
+ "routes.no_routes": "No routes found. Routes are collected after the server starts.",
172
+ "routes.total_routes": "Total Routes",
173
+ "routes.app_routes": "App Routes",
174
+ "routes.application_routes": "Application Routes",
175
+ "routes.framework_debug_routes": "Framework / Debug Routes",
176
+ "routes.method": "Method",
177
+ "routes.path": "Path",
178
+ "routes.middlewares": "Middlewares",
179
+ "routes.none": "None",
180
+
181
+ "routing.matched_route": "Matched Route",
182
+ "routing.route_params": "Route Parameters",
183
+ "routing.query_string": "Query String",
184
+
185
+ "configuration.app_info": "App Info",
186
+ "configuration.env_vars": "Environment Variables",
187
+ "configuration.environment": "Environment",
188
+
189
+ "js.copied": "Copied!"
190
+ }
@@ -0,0 +1,190 @@
1
+ {
2
+ "common.none": "Aucun",
3
+ "common.search": "Rechercher",
4
+ "common.filter_placeholder": "Filtrer…",
5
+ "common.search_ellipsis": "rechercher…",
6
+ "common.key": "Clé",
7
+ "common.value": "Valeur",
8
+ "common.header": "En-tête",
9
+ "common.name": "Nom",
10
+
11
+ "home.hero_sub": "Serveur API REST",
12
+ "home.hero_desc": "Votre serveur est opérationnel. Commencez à effectuer des requêtes vers vos points de terminaison API.",
13
+ "home.running_on_port": "En cours d'exécution sur le port {port}",
14
+ "home.host_label": "Hôte",
15
+ "home.host_sub": "URL de base de l'API",
16
+ "home.node_sub": "Version du runtime",
17
+ "home.app_version_label": "Version de l'application",
18
+ "home.app_version_sub": "Modèle mysqldb",
19
+ "home.environment_label": "Environnement",
20
+ "home.debug_toolbar_section": "Barre d'outils de débogage web",
21
+ "home.request_profiler_label": "Profileur de requêtes",
22
+ "home.request_profiler_desc": "Toutes les requêtes capturées",
23
+ "home.latest_request_label": "Dernière requête",
24
+ "home.latest_request_desc": "Accéder au dernier profil",
25
+ "home.getting_started": "Pour commencer",
26
+ "home.api_ready_title": "L'API REST est prête",
27
+ "home.api_ready_body_1": "Ceci est un serveur API REST — aucune route d'interface utilisateur n'est définie par défaut.",
28
+ "home.api_ready_body_2": "Testez vos points de terminaison avec {code} ou utilisez un client comme Postman, Insomnia ou curl.",
29
+ "home.api_ready_body_3": "Chaque réponse de l'API inclut les en-têtes {token} et {tokenLink} pointant vers le profileur.",
30
+
31
+ "toolbar.profile_token": "Jeton de profil",
32
+ "toolbar.method": "Méthode",
33
+ "toolbar.url": "URL",
34
+ "toolbar.status": "Statut",
35
+ "toolbar.duration": "Durée",
36
+ "toolbar.open_profiler": "Ouvrir le profileur",
37
+ "toolbar.total_time": "Temps total",
38
+ "toolbar.application": "Application",
39
+ "toolbar.database": "Base de données",
40
+ "toolbar.performance": "Performance",
41
+ "toolbar.heap_used": "Mémoire utilisée",
42
+ "toolbar.errors": "Erreurs",
43
+ "toolbar.warnings": "Avertissements",
44
+ "toolbar.deprecations": "Dépréciations",
45
+ "toolbar.logs": "Journaux",
46
+ "toolbar.routing": "Routage",
47
+ "toolbar.http_requests": "Requêtes HTTP",
48
+ "toolbar.last_n_requests": "{n} dernières requêtes HTTP",
49
+ "toolbar.view_all": "Voir tout →",
50
+ "toolbar.no_requests_yet": "Aucune requête pour le moment.",
51
+ "toolbar.server": "Serveur",
52
+ "toolbar.nodejs": "Node.js",
53
+ "toolbar.environment": "Environnement",
54
+ "toolbar.documentation": "📖 Documentation",
55
+ "toolbar.help_support": "❓ Aide & Support",
56
+ "toolbar.open_profiler_emoji": "📊 Ouvrir le profileur",
57
+ "toolbar.profiler": "Profileur",
58
+ "toolbar.minimize": "Réduire la barre d'outils",
59
+ "toolbar.expand": "Agrandir la barre d'outils",
60
+
61
+ "list.search_placeholder": "rechercher méthode, URL, statut, jeton ou IP",
62
+ "list.profile_search": "Recherche de profils",
63
+ "list.search_profiles": "Rechercher des profils",
64
+ "list.latest": "Dernier",
65
+ "list.ip": "IP",
66
+ "list.method": "Méthode",
67
+ "list.status": "Statut",
68
+ "list.search_label": "Recherche",
69
+ "list.token": "Jeton",
70
+ "list.from": "Du",
71
+ "list.until": "Au",
72
+ "list.results": "Résultats",
73
+ "list.search_btn": "Rechercher",
74
+ "list.settings": "Paramètres du profileur",
75
+ "list.http_requests_tab": "Requêtes HTTP",
76
+ "list.console_commands_tab": "Commandes console",
77
+ "list.no_commands": "Aucune commande console n'a encore été exécutée.",
78
+ "list.results_found_one": "{n} résultat trouvé",
79
+ "list.results_found_other": "{n} résultats trouvés",
80
+ "list.col_status": "Statut",
81
+ "list.col_method": "Méthode",
82
+ "list.col_url": "URL",
83
+ "list.col_time": "Heure",
84
+ "list.col_token": "Jeton",
85
+ "list.no_requests_found": "Aucune requête trouvée. Essayez d'ajuster vos filtres.",
86
+ "list.method_any": "Tous",
87
+
88
+ "message.not_found_title": "Profil introuvable",
89
+ "message.not_found_heading": "Profil introuvable",
90
+ "message.not_found_body": "Le token <code>{token}</code> n'existe pas ou a expiré.",
91
+ "message.back_to_list": "← Retour à la liste du profileur",
92
+
93
+ "panel.request": "Requête / Réponse",
94
+ "panel.performance": "Performance",
95
+ "panel.logs": "Journaux",
96
+ "panel.routing": "Routage",
97
+ "panel.configuration": "Configuration",
98
+ "panel.database": "Base de données",
99
+ "panel.exception": "Exception",
100
+ "panel.routes": "Routes",
101
+
102
+ "detail.search_placeholder": "rechercher sur opticorejs.com",
103
+
104
+ "request.tab_request": "Requête",
105
+ "request.tab_response": "Réponse",
106
+ "request.tab_cookies": "Cookies",
107
+ "request.tab_session": "Session",
108
+ "request.tab_server": "Paramètres serveur",
109
+ "request.get_params": "Paramètres GET",
110
+ "request.post_params": "Paramètres POST",
111
+ "request.uploaded_files": "Fichiers téléversés",
112
+ "request.route_params": "Paramètres de route",
113
+ "request.attributes": "Attributs de la requête",
114
+ "request.headers": "En-têtes de la requête",
115
+ "request.content": "Contenu de la requête",
116
+ "request.no_body": "Aucun corps de requête.",
117
+ "request.retry_command": "Commande de réessai",
118
+ "request.copy_curl": "Copier en cURL",
119
+ "request.response_headers": "En-têtes de réponse",
120
+ "request.response_content": "Contenu de la réponse",
121
+ "request.no_response_body": "Aucun corps de réponse capturé.",
122
+ "request.request_cookies": "Cookies de requête",
123
+ "request.response_cookies": "Cookies de réponse",
124
+ "request.session_attributes": "Attributs de session",
125
+ "request.no_session": "Aucune donnée liée à cette section n'a été collectée. Cette application n'utilise pas de sessions.",
126
+ "request.server_params": "Paramètres serveur",
127
+ "panel.http_status": "Statut HTTP",
128
+ "panel.duration": "Durée",
129
+ "panel.memory": "Mémoire",
130
+ "panel.sql_queries": "Requêtes SQL",
131
+
132
+ "performance.title": "Statistiques de performance",
133
+ "performance.total_execution": "Temps d'exécution total",
134
+ "performance.app_time": "Temps applicatif",
135
+ "performance.peak_memory": "Utilisation mémoire maximale",
136
+ "performance.execution_timeline": "Chronologie d'exécution",
137
+ "performance.threshold": "Seuil",
138
+ "performance.threshold_hint": "(la chronologie n'affiche que les événements dont la durée dépasse ce seuil)",
139
+ "performance.application_legend": "application",
140
+ "performance.database_legend": "base de données",
141
+ "performance.no_timing_events": "Aucun événement de temporisation enregistré pour cette requête.",
142
+ "performance.process_info": "Informations sur le processus",
143
+
144
+ "logs.no_logs": "Aucune entrée de journal n'a été enregistrée pour cette requête.",
145
+ "logs.hint": "Utilisez {code} pour ajouter des entrées.",
146
+ "logs.all_messages": "Tous les messages",
147
+ "logs.errors": "Erreurs",
148
+ "logs.deprecations": "Dépréciations",
149
+ "logs.warnings": "Avertissements",
150
+ "logs.time": "Heure",
151
+ "logs.message": "Message",
152
+ "logs.show_context": "Afficher le contexte",
153
+ "logs.hide_context": "Masquer le contexte",
154
+ "logs.show_trace": "Afficher la trace",
155
+
156
+ "database.no_queries": "Aucune requête SQL n'a été enregistrée pour cette requête.",
157
+ "database.hint": "Utilisez {code} dans vos repositories.",
158
+ "database.title": "Requêtes de base de données",
159
+ "database.total_queries": "Requêtes totales",
160
+ "database.total_time": "Temps total",
161
+ "database.avg_per_query": "Moyenne par requête",
162
+ "database.sql_queries": "Requêtes SQL",
163
+ "database.error_badge": "ERREUR",
164
+ "database.bindings": "Paramètres :",
165
+ "database.error_label": "Erreur :",
166
+
167
+ "exception.title": "Exceptions",
168
+ "exception.no_exceptions": "Aucune exception n'a été levée pendant cette requête.",
169
+
170
+ "routes.title": "Routes",
171
+ "routes.no_routes": "Aucune route trouvée. Les routes sont collectées après le démarrage du serveur.",
172
+ "routes.total_routes": "Routes totales",
173
+ "routes.app_routes": "Routes de l'application",
174
+ "routes.application_routes": "Routes de l'application",
175
+ "routes.framework_debug_routes": "Routes framework / débogage",
176
+ "routes.method": "Méthode",
177
+ "routes.path": "Chemin",
178
+ "routes.middlewares": "Middlewares",
179
+ "routes.none": "Aucune",
180
+
181
+ "routing.matched_route": "Route correspondante",
182
+ "routing.route_params": "Paramètres de route",
183
+ "routing.query_string": "Chaîne de requête",
184
+
185
+ "configuration.app_info": "Infos application",
186
+ "configuration.env_vars": "Variables d'environnement",
187
+ "configuration.environment": "Environnement",
188
+
189
+ "js.copied": "Copié !"
190
+ }
@@ -0,0 +1,120 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>OptiCoreJs</title>
7
+ <link rel="stylesheet" href="/_profiler/assets/css/home-page.css">
8
+ </head>
9
+ <body class="{{ 'has-toolbar' if showToolbar }}">
10
+ <div class="page">
11
+ <div class="container">
12
+
13
+ <!-- HERO -->
14
+ <div class="hero">
15
+ <div class="hero-logo">
16
+ <div class="hero-icon">OP</div>
17
+ <div>
18
+ <div class="hero-name">OptiCoreJs</div>
19
+ <div class="hero-sub">API REST Server</div>
20
+ </div>
21
+ </div>
22
+ <div class="hero-desc">{{ t('home.hero_desc') }}</div>
23
+ </div>
24
+
25
+ <!-- STATUS -->
26
+ <div class="status-row">
27
+ <div class="status-pill">
28
+ <span class="status-dot"></span>
29
+ {{ t('home.running_on_port', { port: port }) }}
30
+ </div>
31
+ </div>
32
+
33
+ <!-- CARDS -->
34
+ <div class="cards">
35
+ <div class="card">
36
+ <div class="card-label">{{ t('home.host_label') }}</div>
37
+ <div class="card-value">{{ host }}:{{ port }}</div>
38
+ <div class="card-sub">{{ t('home.host_sub') }}</div>
39
+ </div>
40
+ <div class="card">
41
+ <div class="card-label">{{ t('toolbar.nodejs') }}</div>
42
+ <div class="card-value">{{ nodeVersion }}</div>
43
+ <div class="card-sub">{{ t('home.node_sub') }}</div>
44
+ </div>
45
+ <div class="card">
46
+ <div class="card-label">{{ t('home.app_version_label') }}</div>
47
+ <div class="card-value">v{{ appVersion }}</div>
48
+ <div class="card-sub">{{ t('home.app_version_sub') }}</div>
49
+ </div>
50
+ <div class="card">
51
+ <div class="card-label">{{ t('home.environment_label') }}</div>
52
+ <div class="card-value card-value--{{ 'prod' if isProd else 'dev' }}">{{ environment }}</div>
53
+ <div class="card-sub">NODE_ENV</div>
54
+ </div>
55
+ </div>
56
+
57
+ {% if showToolbar %}
58
+ <!-- PROFILER QUICK LINKS -->
59
+ <div class="section">
60
+ <div class="section-title">{{ t('home.debug_toolbar_section') }}</div>
61
+ <div class="quick-links">
62
+ <a href="/_profiler" class="quick-link">
63
+ <div class="quick-link-icon" style="background:#FDE8D4;">
64
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#FAC68E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="22"/><polygon points="12,5 19,5 22,9 19,13 12,13"/><polygon points="12,14 5,14 2,18 5,21 12,21"/></svg>
65
+ </div>
66
+ <div>
67
+ <div class="quick-link-label">{{ t('home.request_profiler_label') }}</div>
68
+ <div class="quick-link-desc">{{ t('home.request_profiler_desc') }}</div>
69
+ </div>
70
+ </a>
71
+ <a href="/_profiler/latest" class="quick-link">
72
+ <div class="quick-link-icon" style="background:#FDE8D4;">
73
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#FAC68E" stroke-width="2"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>
74
+ </div>
75
+ <div>
76
+ <div class="quick-link-label">{{ t('home.latest_request_label') }}</div>
77
+ <div class="quick-link-desc">{{ t('home.latest_request_desc') }}</div>
78
+ </div>
79
+ </a>
80
+ </div>
81
+ </div>
82
+ {% endif %}
83
+
84
+ <!-- API NOTICE -->
85
+ <div class="section">
86
+ <div class="section-title">{{ t('home.getting_started') }}</div>
87
+ <div class="api-notice">
88
+ <div class="api-notice-icon">🚀</div>
89
+ <div>
90
+ <div class="api-notice-title">{{ t('home.api_ready_title') }}</div>
91
+ <div class="api-notice-body">
92
+ {{ t('home.api_ready_body_1') }}
93
+ {{ t('home.api_ready_body_2', { code: safeHtml('<code>GET http://' + host + ':' + port + '/users</code>') }) | safe }}
94
+ {% if showToolbar %}{{ t('home.api_ready_body_3', { token: safeHtml('<code>X-Debug-Token</code>'), tokenLink: safeHtml('<code>X-Debug-Token-Link</code>') }) | safe }}{% endif %}
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- CANVAS particules sable/désert -->
103
+ <canvas id="op-canvas" aria-hidden="true"></canvas>
104
+
105
+ <!-- VAGUES SVG : 3 couches à profondeurs différentes, phase décalée -->
106
+ <div class="op-waves" aria-hidden="true">
107
+ <svg class="op-wave op-wave-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2880 500" preserveAspectRatio="none">
108
+ <path d="M 0,0 L 2880,0 L 2880,360 C 2640,470 2400,470 2160,360 C 1920,195 1680,195 1440,360 C 1200,470 960,470 720,360 C 480,195 240,195 0,360 Z" fill="#FAC68E" fill-opacity="0.38"/>
109
+ </svg>
110
+ <svg class="op-wave op-wave-2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2880 500" preserveAspectRatio="none">
111
+ <path d="M 0,0 L 2880,0 L 2880,265 C 2640,375 2400,375 2160,265 C 1920,130 1680,130 1440,265 C 1200,375 960,375 720,265 C 480,130 240,130 0,265 Z" fill="#FAC68E" fill-opacity="0.32"/>
112
+ </svg>
113
+ <svg class="op-wave op-wave-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2880 500" preserveAspectRatio="none">
114
+ <path d="M 0,0 L 2880,0 L 2880,165 C 2640,265 2400,265 2160,165 C 1920,60 1680,60 1440,165 C 1200,265 960,265 720,165 C 480,60 240,60 0,165 Z" fill="#FAC68E" fill-opacity="0.45"/>
115
+ </svg>
116
+ </div>
117
+
118
+ <script src="/_profiler/assets/js/home-particles.js"></script>
119
+ </body>
120
+ </html>
@@ -0,0 +1,19 @@
1
+ {% macro icon(name) %}
2
+ {%- if name == "clock" -%}
3
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
4
+ {%- elif name == "db" -%}
5
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg>
6
+ {%- elif name == "route" -%}
7
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="2" x2="12" y2="22"/><polygon points="12,5 19,5 22,9 19,13 12,13"/><polygon points="12,14 5,14 2,18 5,21 12,21"/></svg>
8
+ {%- elif name == "log" -%}
9
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
10
+ {%- elif name == "perf" -%}
11
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg>
12
+ {%- elif name == "config" -%}
13
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/></svg>
14
+ {%- elif name == "exception" -%}
15
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>
16
+ {%- elif name == "map" -%}
17
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="1 6 1 22 8 18 16 22 23 18 23 2 16 6 8 2 1 6"/><line x1="8" y1="2" x2="8" y2="18"/><line x1="16" y1="6" x2="16" y2="22"/></svg>
18
+ {%- endif -%}
19
+ {% endmacro %}
@@ -0,0 +1,17 @@
1
+ {% macro kv_table(rows, emptyLabel=None, headers=None) %}
2
+ {%- if rows.length == 0 -%}
3
+ <div class="empty-state"><div class="empty-text">{{ emptyLabel if emptyLabel else t('common.none') }}</div></div>
4
+ {%- else -%}
5
+ <table class="data-table">
6
+ {%- if headers %}<thead><tr>{% for h in headers %}<th>{{ h }}</th>{% endfor %}</tr></thead>{% endif %}
7
+ <tbody>
8
+ {%- for row in rows %}
9
+ <tr>
10
+ <td class="key-col">{{ row.key }}</td>
11
+ <td class="val-col"{% if row.title %} title="{{ row.title }}"{% endif %}{% if row.truncate %} style="max-width:500px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;"{% endif %}>{% if row.badge %}<span class="badge badge-{{ row.badge.variant }}">{{ row.badge.text }}</span>{% elif row.code %}<code>{{ row.value }}</code>{% else %}{{ row.value }}{% endif %}</td>
12
+ </tr>
13
+ {%- endfor %}
14
+ </tbody>
15
+ </table>
16
+ {%- endif -%}
17
+ {% endmacro %}
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>{{ t(titleKey) if titleKey else title }}</title>
7
+ <link rel="stylesheet" href="/_profiler/assets/css/debug-message.css">
8
+ </head>
9
+ <body>
10
+ {% if headingKey or heading %}<div class="msg-title">{{ t(headingKey) if headingKey else heading }}</div>{% endif %}
11
+ <div>{{ (t(messageKey, messageParams) if messageKey else message) | safe }}</div>
12
+ {% if link %}<a href="{{ link.href }}">{{ t(link.labelKey) if link.labelKey else link.label }}</a>{% endif %}
13
+ </body>
14
+ </html>
@@ -0,0 +1,16 @@
1
+ {% import "macros/icons.njk" as icons %}
2
+ {% from "macros/tables.njk" import kv_table with context %}
3
+ <div class="panel-title">{{ icons.icon("config") }} {{ t('panel.configuration') }}</div>
4
+
5
+ <div class="panel-section">
6
+ <div class="panel-section-title">{{ t('configuration.app_info') }}</div>
7
+ <div class="table-wrap">{{ kv_table(panelData.appInfo) }}</div>
8
+ </div>
9
+
10
+ <div class="panel-section">
11
+ <div class="panel-section-title">{{ t('configuration.env_vars') }} <span class="badge badge-muted">{{ panelData.envCount }}</span></div>
12
+ <div class="table-wrap">
13
+ <div class="table-wrap-title">{{ t('configuration.environment') }} <input type="search" class="table-search" placeholder="{{ t('common.filter_placeholder') }}" oninput="filterTable(this)"></div>
14
+ {{ kv_table(panelData.envRows, headers=[t('common.key'), t('common.value')]) }}
15
+ </div>
16
+ </div>
@@ -0,0 +1,36 @@
1
+ {% import "macros/icons.njk" as icons %}
2
+ {% if not panelData.hasQueries %}
3
+ <div class="panel-title">{{ icons.icon("db") }} {{ t('panel.database') }}</div>
4
+ <div class="empty-state">
5
+ <div class="empty-icon">🗄</div>
6
+ <div class="empty-text">{{ t('database.no_queries') }}</div>
7
+ <div style="font-size:12px;color:#555;margin-top:8px;">
8
+ {{ t('database.hint', { code: safeHtml('<code>sqlCollector.record(token, { sql, duration })</code>') }) | safe }}
9
+ </div>
10
+ </div>
11
+ {% else %}
12
+ <div class="panel-title">{{ icons.icon("db") }} {{ t('database.title') }}</div>
13
+
14
+ <div class="stats-grid">
15
+ <div class="stat-card"><div class="stat-value">{{ panelData.totalCount }}</div><div class="stat-label">{{ t('database.total_queries') }}</div></div>
16
+ <div class="stat-card"><div class="stat-value">{{ panelData.totalTimeFormatted }}</div><div class="stat-label">{{ t('database.total_time') }}</div></div>
17
+ <div class="stat-card"><div class="stat-value">{{ panelData.avgFormatted }}</div><div class="stat-label">{{ t('database.avg_per_query') }}</div></div>
18
+ </div>
19
+
20
+ <div class="table-wrap">
21
+ <div class="table-wrap-title">{{ icons.icon("db") }} {{ t('database.sql_queries') }} <span class="badge badge-info">{{ panelData.totalCount }}</span></div>
22
+ {% for q in panelData.queries %}
23
+ <div class="sql-block">
24
+ <div class="sql-meta">
25
+ <span class="sql-type sql-type-{{ q.type }}">{{ q.type }}</span>
26
+ <span style="color:#666;font-size:11px;">#{{ q.index }}</span>
27
+ {% if q.hasError %}<span class="badge badge-warn">{{ t('database.error_badge') }}</span>{% endif %}
28
+ <span class="sql-duration">{{ icons.icon("clock") }} {{ q.durationFormatted }}</span>
29
+ </div>
30
+ <pre class="sql-code">{{ q.sql }}</pre>
31
+ {% if q.bindingsJson %}<div style="margin-top:6px;font-size:11px;color:#666;">{{ t('database.bindings') }} <code>{{ q.bindingsJson }}</code></div>{% endif %}
32
+ {% if q.error %}<div style="color:#e74c3c;font-size:11px;margin-top:4px;">{{ t('database.error_label') }} {{ q.error }}</div>{% endif %}
33
+ </div>
34
+ {% endfor %}
35
+ </div>
36
+ {% endif %}
@@ -0,0 +1,26 @@
1
+ {% import "macros/icons.njk" as icons %}
2
+ <div class="panel-title">{{ icons.icon("exception") }} {{ t('exception.title') }}</div>
3
+ {% if panelData.noException %}
4
+ <div class="empty-state">
5
+ <div class="empty-icon">✅</div>
6
+ <div class="empty-text">{{ t('exception.no_exceptions') }}</div>
7
+ </div>
8
+ {% else %}
9
+ {% if panelData.showBanner %}
10
+ <div style="background:var(--warn-bg);border:1px solid #c62828;border-radius:6px;padding:16px 20px;margin-bottom:20px;">
11
+ <div style="font-weight:700;font-size:13px;color:#e74c3c;margin-bottom:6px;">
12
+ {{ icons.icon("exception") }} HTTP {{ panelData.statusCode }} — {{ panelData.statusMessage }}
13
+ </div>
14
+ <div style="font-family:monospace;font-size:12px;color:#999;">
15
+ {{ panelData.method }} {{ panelData.url }}
16
+ </div>
17
+ </div>
18
+ {% endif %}
19
+ {% for l in panelData.errLogs %}
20
+ <div style="background:#1e1a1a;border:1px solid #c62828;border-radius:6px;padding:14px 18px;margin-bottom:12px;">
21
+ <div style="font-weight:700;font-size:13px;color:#e74c3c;margin-bottom:6px;">{{ l.levelUpper }}</div>
22
+ <pre style="font-size:12px;color:#e0e0e0;white-space:pre-wrap;">{{ l.message }}</pre>
23
+ {% if l.contextJson %}<pre class="json-pre" style="margin-top:10px;">{{ l.contextJson | safe }}</pre>{% endif %}
24
+ </div>
25
+ {% endfor %}
26
+ {% endif %}