vako 1.3.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.
- package/CHANGELOG.md +63 -0
- package/README.md +1944 -0
- package/bin/commands/quick-setup.js +111 -0
- package/bin/commands/setup-executor.js +203 -0
- package/bin/commands/setup.js +737 -0
- package/bin/create-veko-app.js +75 -0
- package/bin/veko-update.js +205 -0
- package/bin/veko.js +188 -0
- package/error/error.ejs +382 -0
- package/index.js +36 -0
- package/lib/adapters/nextjs-adapter.js +241 -0
- package/lib/app.js +749 -0
- package/lib/core/auth-manager.js +1353 -0
- package/lib/core/auto-updater.js +1118 -0
- package/lib/core/logger.js +97 -0
- package/lib/core/module-installer.js +86 -0
- package/lib/dev/dev-server.js +292 -0
- package/lib/layout/layout-manager.js +834 -0
- package/lib/plugin-manager.js +1795 -0
- package/lib/routing/route-manager.js +1000 -0
- package/package.json +231 -0
- package/templates/public/css/style.css +2 -0
- package/templates/public/js/main.js +1 -0
- package/tsconfig.json +50 -0
- package/types/index.d.ts +238 -0
package/error/error.ejs
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="<%= locals.lang || 'en' %>">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title><%= (locals.lang === 'fr' ? 'Erreur' : 'Error') %> <%= status || 500 %> - Veko.js</title>
|
|
7
|
+
<script src="https://cdn.tailwindcss.com"></script>
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
|
11
|
+
<script>
|
|
12
|
+
tailwind.config = {
|
|
13
|
+
theme: {
|
|
14
|
+
extend: {
|
|
15
|
+
fontFamily: {
|
|
16
|
+
sans: ['Inter', 'sans-serif'],
|
|
17
|
+
mono: ['"JetBrains Mono"', 'monospace'],
|
|
18
|
+
},
|
|
19
|
+
animation: {
|
|
20
|
+
'pulse-slow': 'pulse 4s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
|
21
|
+
'float': 'float 3s ease-in-out infinite',
|
|
22
|
+
'bounceSlow': 'bounce 2s infinite',
|
|
23
|
+
},
|
|
24
|
+
keyframes: {
|
|
25
|
+
float: {
|
|
26
|
+
'0%, 100%': { transform: 'translateY(0px)' },
|
|
27
|
+
'50%': { transform: 'translateY(-10px)' },
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
plugins: [],
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
<style>
|
|
36
|
+
/* Custom styles for animations and special effects */
|
|
37
|
+
.glow {
|
|
38
|
+
text-shadow: 0 0 5px rgba(255,255,255,0.7);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.code-highlight {
|
|
42
|
+
position: relative;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.code-error-line {
|
|
46
|
+
background: rgba(239, 68, 68, 0.2);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.error-pointer {
|
|
50
|
+
color: rgb(239, 68, 68);
|
|
51
|
+
font-weight: bold;
|
|
52
|
+
animation: bounce 1s infinite;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@keyframes gradientBg {
|
|
56
|
+
0% { background-position: 0% 50% }
|
|
57
|
+
50% { background-position: 100% 50% }
|
|
58
|
+
100% { background-position: 0% 50% }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.animated-gradient {
|
|
62
|
+
background: linear-gradient(270deg, #f87171, #f472b6, #c084fc, #818cf8);
|
|
63
|
+
background-size: 800% 800%;
|
|
64
|
+
animation: gradientBg 8s ease infinite;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Syntax highlighting for HTML/EJS */
|
|
68
|
+
.syntax-tag { color: #79b8ff; }
|
|
69
|
+
.syntax-attr { color: #b392f0; }
|
|
70
|
+
.syntax-string { color: #9ecbff; }
|
|
71
|
+
.syntax-comment { color: #6a737d; font-style: italic; }
|
|
72
|
+
.syntax-ejs { color: #f97583; background-color: rgba(249, 117, 131, 0.1); border-radius: 2px; }
|
|
73
|
+
|
|
74
|
+
/* Improved error highlighting */
|
|
75
|
+
.error-highlight {
|
|
76
|
+
background-color: rgba(255, 0, 0, 0.2);
|
|
77
|
+
border-radius: 2px;
|
|
78
|
+
padding: 0 2px;
|
|
79
|
+
}
|
|
80
|
+
</style>
|
|
81
|
+
</head>
|
|
82
|
+
|
|
83
|
+
<body class="bg-slate-900 text-white min-h-screen">
|
|
84
|
+
<!-- Visual background effect -->
|
|
85
|
+
<div class="fixed inset-0 opacity-10">
|
|
86
|
+
<div class="absolute inset-0 overflow-hidden">
|
|
87
|
+
<div class="absolute inset-0 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-from)_0%,_var(--tw-gradient-to)_100%)] from-violet-600/20 to-transparent"></div>
|
|
88
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="absolute top-0 left-0 w-full h-full">
|
|
89
|
+
<defs>
|
|
90
|
+
<pattern id="grid-pattern" width="40" height="40" patternUnits="userSpaceOnUse">
|
|
91
|
+
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="1"></path>
|
|
92
|
+
</pattern>
|
|
93
|
+
</defs>
|
|
94
|
+
<rect width="100%" height="100%" fill="url(#grid-pattern)"></rect>
|
|
95
|
+
</svg>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<div class="relative min-h-screen flex flex-col">
|
|
100
|
+
<!-- Header -->
|
|
101
|
+
<header class="py-6 px-4 sm:px-6 lg:px-8 animated-gradient">
|
|
102
|
+
<div class="max-w-7xl mx-auto flex flex-col sm:flex-row justify-between items-center">
|
|
103
|
+
<div class="flex items-center space-x-3 mb-4 sm:mb-0">
|
|
104
|
+
<div class="text-3xl animate-float"><%= errorType?.icon || '❌' %></div>
|
|
105
|
+
<div>
|
|
106
|
+
<h1 class="text-2xl font-bold tracking-tight">Veko.js</h1>
|
|
107
|
+
<p class="text-sm text-white/70"><%= locals.lang === 'fr' ? 'Framework développement' : 'Development Framework' %></p>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="flex items-center gap-4">
|
|
111
|
+
<div class="flex space-x-2">
|
|
112
|
+
<button onclick="changeLanguage('en')" class="px-2 py-1 rounded-md text-xs <%= locals.lang !== 'fr' ? 'bg-white/20 text-white' : 'text-white/60 hover:text-white' %>">EN</button>
|
|
113
|
+
<button onclick="changeLanguage('fr')" class="px-2 py-1 rounded-md text-xs <%= locals.lang === 'fr' ? 'bg-white/20 text-white' : 'text-white/60 hover:text-white' %>">FR</button>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="bg-white/10 backdrop-blur-sm rounded-full px-4 py-2 text-sm flex items-center border border-white/20">
|
|
116
|
+
<span class="mr-2 h-2 w-2 rounded-full bg-red-500 animate-pulse inline-block"></span>
|
|
117
|
+
<span><%= locals.lang === 'fr' ? 'Erreur' : 'Error' %> <%= status || 500 %></span>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</header>
|
|
122
|
+
|
|
123
|
+
<!-- Main content -->
|
|
124
|
+
<main class="flex-grow py-8 px-4 sm:px-6 lg:px-8">
|
|
125
|
+
<div class="max-w-7xl mx-auto">
|
|
126
|
+
<!-- Main error section -->
|
|
127
|
+
<div class="bg-white/5 backdrop-blur-sm border border-white/10 rounded-xl overflow-hidden shadow-2xl mb-8">
|
|
128
|
+
<div class="p-8">
|
|
129
|
+
<h2 class="text-2xl md:text-4xl font-bold mb-2 flex items-center flex-wrap">
|
|
130
|
+
<%= locals.lang === 'fr' ? errorTypeName.fr : errorTypeName.en %>
|
|
131
|
+
<span class="bg-white/10 text-white/70 text-sm rounded-full px-3 py-1 ml-3"><%= status || 500 %></span>
|
|
132
|
+
</h2>
|
|
133
|
+
<p class="mb-6 text-lg text-white/70">
|
|
134
|
+
<%= message || (locals.lang === 'fr' ? 'Une erreur inattendue s\'est produite' : 'An unexpected error occurred') %>
|
|
135
|
+
</p>
|
|
136
|
+
|
|
137
|
+
<!-- Action buttons -->
|
|
138
|
+
<div class="flex flex-wrap gap-4 mb-8">
|
|
139
|
+
<button onclick="window.history.back()" class="px-5 py-3 bg-indigo-600 hover:bg-indigo-700 rounded-xl text-white font-medium transition-all flex items-center">
|
|
140
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
141
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
|
142
|
+
</svg>
|
|
143
|
+
<%= locals.lang === 'fr' ? 'Retour' : 'Back' %>
|
|
144
|
+
</button>
|
|
145
|
+
<button onclick="window.location.reload()" class="px-5 py-3 bg-emerald-600 hover:bg-emerald-700 rounded-xl text-white font-medium transition-all flex items-center">
|
|
146
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
147
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
148
|
+
</svg>
|
|
149
|
+
<%= locals.lang === 'fr' ? 'Réessayer' : 'Retry' %>
|
|
150
|
+
</button>
|
|
151
|
+
<a href="/" class="px-5 py-3 bg-white/10 hover:bg-white/20 rounded-xl text-white font-medium transition-all flex items-center">
|
|
152
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
153
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7m-14 0l2 2m0 0l7 7 7-7m-14 0l2-2" />
|
|
154
|
+
</svg>
|
|
155
|
+
<%= locals.lang === 'fr' ? 'Accueil' : 'Home' %>
|
|
156
|
+
</a>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<% if (diagnostics && diagnostics.codeContext) { %>
|
|
161
|
+
<!-- Source code section -->
|
|
162
|
+
<div class="border-t border-white/10">
|
|
163
|
+
<div class="p-5">
|
|
164
|
+
<div class="flex items-center justify-between mb-3">
|
|
165
|
+
<h3 class="font-semibold text-white text-lg"><%= locals.lang === 'fr' ? 'Source de l\'erreur' : 'Error source' %></h3>
|
|
166
|
+
<span class="bg-white/10 px-3 py-1 rounded-full text-xs text-white/70"><%= locals.lang === 'fr' ? 'Ligne' : 'Line' %> <%= diagnostics.codeContext.line %>, <%= locals.lang === 'fr' ? 'Col' : 'Col' %> <%= diagnostics.codeContext.column %></span>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="mb-2 text-white/70 text-sm">
|
|
169
|
+
<code class="font-mono"><%= diagnostics.codeContext.file %></code>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<!-- Code with syntax highlighting -->
|
|
174
|
+
<div class="overflow-x-auto bg-slate-800 rounded-lg">
|
|
175
|
+
<table class="min-w-full">
|
|
176
|
+
<tbody class="font-mono text-sm">
|
|
177
|
+
<% diagnostics.codeContext.codeLines.forEach(line => { %>
|
|
178
|
+
<tr class="<%= line.isError ? 'code-error-line' : '' %>">
|
|
179
|
+
<td class="text-right px-4 py-1 select-none text-white/40 border-r border-white/5">
|
|
180
|
+
<%= line.number %>
|
|
181
|
+
</td>
|
|
182
|
+
<td class="px-4 py-1 overflow-x-auto">
|
|
183
|
+
<% if (line.isError && line.content.includes('<') && line.content.includes('>')) { %>
|
|
184
|
+
<%- highlightHtml(line.content, line.highlightColumn) %>
|
|
185
|
+
<% } else if (line.isError && line.highlightColumn) { %>
|
|
186
|
+
<%
|
|
187
|
+
const beforeError = escapeHtml(line.content.substring(0, line.highlightColumn));
|
|
188
|
+
const atError = escapeHtml(line.content.substring(line.highlightColumn, line.highlightColumn + 1)) || ' ';
|
|
189
|
+
const afterError = escapeHtml(line.content.substring(line.highlightColumn + 1));
|
|
190
|
+
%>
|
|
191
|
+
<span><%= beforeError %></span>
|
|
192
|
+
<span class="bg-red-500/30 text-white px-0.5"><%= atError %></span>
|
|
193
|
+
<span><%= afterError %></span>
|
|
194
|
+
<% } else { %>
|
|
195
|
+
<% if (line.content.includes('<') && line.content.includes('>')) { %>
|
|
196
|
+
<%- highlightHtml(line.content) %>
|
|
197
|
+
<% } else { %>
|
|
198
|
+
<%= escapeHtml(line.content) %>
|
|
199
|
+
<% } %>
|
|
200
|
+
<% } %>
|
|
201
|
+
</td>
|
|
202
|
+
</tr>
|
|
203
|
+
<% if (line.isError) { %>
|
|
204
|
+
<tr>
|
|
205
|
+
<td class="px-4 border-r border-white/5"></td>
|
|
206
|
+
<td class="px-4 py-1">
|
|
207
|
+
<div class="flex" style="margin-left: <%= Math.min(line.highlightColumn, 50) %>px">
|
|
208
|
+
<span class="text-red-500 animate-bounceSlow error-pointer">^</span>
|
|
209
|
+
<span class="text-red-400 ml-2"><%= message %></span>
|
|
210
|
+
</div>
|
|
211
|
+
</td>
|
|
212
|
+
</tr>
|
|
213
|
+
<% } %>
|
|
214
|
+
<% }); %>
|
|
215
|
+
</tbody>
|
|
216
|
+
</table>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<% } %>
|
|
220
|
+
|
|
221
|
+
<!-- Stack Trace section if available -->
|
|
222
|
+
<% if (showStack && error.stack) { %>
|
|
223
|
+
<div class="border-t border-white/10">
|
|
224
|
+
<div class="p-5">
|
|
225
|
+
<h3 class="font-semibold text-lg mb-3 flex items-center">
|
|
226
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
227
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10" />
|
|
228
|
+
</svg>
|
|
229
|
+
Stack Trace
|
|
230
|
+
</h3>
|
|
231
|
+
<div class="bg-slate-800 rounded-lg p-4 overflow-x-auto">
|
|
232
|
+
<pre class="font-mono text-sm text-white/70 whitespace-pre-wrap"><%= error.stack %></pre>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
<% } %>
|
|
237
|
+
|
|
238
|
+
<!-- Environment -->
|
|
239
|
+
<% if (diagnostics && diagnostics.platform) { %>
|
|
240
|
+
<div class="border-t border-white/10 p-5">
|
|
241
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
242
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
243
|
+
<h4 class="text-sm font-medium text-white/70 mb-1">Node.js</h4>
|
|
244
|
+
<p class="font-mono text-sm"><%= diagnostics.platform.node %></p>
|
|
245
|
+
</div>
|
|
246
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
247
|
+
<h4 class="text-sm font-medium text-white/70 mb-1"><%= locals.lang === 'fr' ? 'Système' : 'System' %></h4>
|
|
248
|
+
<p class="font-mono text-sm"><%= diagnostics.platform.os %></p>
|
|
249
|
+
</div>
|
|
250
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
251
|
+
<h4 class="text-sm font-medium text-white/70 mb-1">Timestamp</h4>
|
|
252
|
+
<p class="font-mono text-sm"><%= diagnostics.timestamp %></p>
|
|
253
|
+
</div>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
<% } %>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<!-- FAQ section -->
|
|
260
|
+
<div class="bg-white/5 backdrop-blur-sm border border-white/10 rounded-xl overflow-hidden shadow-lg">
|
|
261
|
+
<div class="p-6">
|
|
262
|
+
<h3 class="text-xl font-semibold mb-4"><%= locals.lang === 'fr' ? 'Solutions possibles' : 'Possible solutions' %></h3>
|
|
263
|
+
|
|
264
|
+
<div class="space-y-4">
|
|
265
|
+
<% if (status === 404) { %>
|
|
266
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
267
|
+
<h4 class="font-medium mb-2"><%= locals.lang === 'fr' ? 'Vérifier l\'URL' : 'Verify the URL' %></h4>
|
|
268
|
+
<p class="text-white/70"><%= locals.lang === 'fr' ? 'Assurez-vous que l\'URL demandée existe et est correctement écrite.' : 'Make sure the requested URL exists and is correctly typed.' %></p>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
271
|
+
<h4 class="font-medium mb-2"><%= locals.lang === 'fr' ? 'Vérifier les routes' : 'Check routes' %></h4>
|
|
272
|
+
<p class="text-white/70"><%= locals.lang === 'fr' ? 'Assurez-vous que la route est définie dans votre routeur.' : 'Make sure the route is defined in your router.' %></p>
|
|
273
|
+
<pre class="bg-slate-800 p-3 mt-2 rounded font-mono text-sm">router.get('/path', controller.method);</pre>
|
|
274
|
+
</div>
|
|
275
|
+
<% } else if (status === 500) { %>
|
|
276
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
277
|
+
<h4 class="font-medium mb-2"><%= locals.lang === 'fr' ? 'Vérifier le code' : 'Check the code' %></h4>
|
|
278
|
+
<p class="text-white/70"><%= locals.lang === 'fr' ? 'L\'erreur est probablement due à un bug dans le code. Consultez le message et la stack trace pour localiser le problème.' : 'The error is likely due to a bug in the code. Check the message and stack trace to locate the problem.' %></p>
|
|
279
|
+
</div>
|
|
280
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
281
|
+
<h4 class="font-medium mb-2"><%= locals.lang === 'fr' ? 'Vérifier les dépendances' : 'Check dependencies' %></h4>
|
|
282
|
+
<p class="text-white/70"><%= locals.lang === 'fr' ? 'Une dépendance manquante ou obsolète pourrait causer ce problème. Essayez de réinstaller les dépendances.' : 'A missing or outdated dependency could cause this issue. Try reinstalling dependencies.' %></p>
|
|
283
|
+
<pre class="bg-slate-800 p-3 mt-2 rounded font-mono text-sm">npm install</pre>
|
|
284
|
+
</div>
|
|
285
|
+
<% } else { %>
|
|
286
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
287
|
+
<h4 class="font-medium mb-2"><%= locals.lang === 'fr' ? 'Consulter la documentation' : 'Check the documentation' %></h4>
|
|
288
|
+
<p class="text-white/70"><%= locals.lang === 'fr' ? 'Pour plus d\'informations sur cette erreur et comment la résoudre, consultez la documentation de Veko.js.' : 'For more information about this error and how to solve it, check the Veko.js documentation.' %></p>
|
|
289
|
+
</div>
|
|
290
|
+
<% } %>
|
|
291
|
+
<div class="p-4 bg-white/5 rounded-lg">
|
|
292
|
+
<h4 class="font-medium mb-2"><%= locals.lang === 'fr' ? 'Redémarrer le serveur' : 'Restart the server' %></h4>
|
|
293
|
+
<p class="text-white/70"><%= locals.lang === 'fr' ? 'Parfois, simplement redémarrer le serveur peut résoudre le problème.' : 'Sometimes, simply restarting the server can solve the problem.' %></p>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</main>
|
|
300
|
+
|
|
301
|
+
<!-- Footer -->
|
|
302
|
+
<footer class="py-6 px-4 sm:px-6 lg:px-8 bg-gradient-to-b from-transparent to-black/20">
|
|
303
|
+
<div class="max-w-7xl mx-auto text-center text-sm text-white/50">
|
|
304
|
+
<p>Veko.js Framework • <span class="bg-white/10 px-2 py-1 rounded text-xs"><%= env %></span></p>
|
|
305
|
+
<p class="mt-2 text-xs">
|
|
306
|
+
<%= locals.lang === 'fr' ? 'Pour désactiver cette page d\'erreur détaillée en production, définissez' : 'To disable this detailed error page in production, set' %> <code class="bg-white/10 px-1 rounded">NODE_ENV=production</code>
|
|
307
|
+
</p>
|
|
308
|
+
</div>
|
|
309
|
+
</footer>
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
<!-- Floating notification -->
|
|
313
|
+
<div id="notification-container" class="fixed top-4 right-4 z-50 space-y-4 max-w-sm w-full"></div>
|
|
314
|
+
|
|
315
|
+
<script>
|
|
316
|
+
// Function to display a notification
|
|
317
|
+
function showNotification(message, type = 'info') {
|
|
318
|
+
const container = document.getElementById('notification-container');
|
|
319
|
+
const colors = {
|
|
320
|
+
info: 'bg-blue-500',
|
|
321
|
+
success: 'bg-green-500',
|
|
322
|
+
warning: 'bg-yellow-500',
|
|
323
|
+
error: 'bg-red-500'
|
|
324
|
+
};
|
|
325
|
+
const icons = {
|
|
326
|
+
info: '<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>',
|
|
327
|
+
success: '<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>',
|
|
328
|
+
warning: '<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" /></svg>',
|
|
329
|
+
error: '<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>'
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const notification = document.createElement('div');
|
|
333
|
+
notification.className = `transform translate-x-full transition-all duration-500 ${colors[type]} text-white rounded-lg shadow-lg overflow-hidden`;
|
|
334
|
+
notification.innerHTML = `
|
|
335
|
+
<div class="flex items-center p-4">
|
|
336
|
+
<div class="flex-shrink-0">
|
|
337
|
+
${icons[type]}
|
|
338
|
+
</div>
|
|
339
|
+
<div class="ml-3 flex-1">
|
|
340
|
+
<p class="text-sm font-medium">${message}</p>
|
|
341
|
+
</div>
|
|
342
|
+
<div class="ml-4 flex-shrink-0 flex">
|
|
343
|
+
<button class="inline-flex text-white focus:outline-none focus:text-gray-300">
|
|
344
|
+
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
345
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
346
|
+
</svg>
|
|
347
|
+
</button>
|
|
348
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
<div class="bg-white/20 h-1"><div class="bg-white h-1 animate-[shrink_5s_linear]"></div></div>
|
|
351
|
+
`;
|
|
352
|
+
|
|
353
|
+
container.appendChild(notification);
|
|
354
|
+
|
|
355
|
+
setTimeout(() => {
|
|
356
|
+
notification.style.transform = 'translateX(0)';
|
|
357
|
+
}, 100);
|
|
358
|
+
|
|
359
|
+
const closeButton = notification.querySelector('button');
|
|
360
|
+
closeButton.addEventListener('click', () => {
|
|
361
|
+
notification.style.transform = 'translateX(full)';
|
|
362
|
+
setTimeout(() => notification.remove(), 500);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
setTimeout(() => {
|
|
366
|
+
notification.style.transform = 'translateX(full)';
|
|
367
|
+
setTimeout(() => notification.remove(), 500);
|
|
368
|
+
}, 5000);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Language switcher
|
|
372
|
+
function changeLanguage(lang) {
|
|
373
|
+
const url = new URL(window.location.href);
|
|
374
|
+
url.searchParams.set('lang', lang);
|
|
375
|
+
window.location.href = url.toString();
|
|
376
|
+
}
|
|
377
|
+
</script>
|
|
378
|
+
|
|
379
|
+
<!-- Auto-refresh script injected automatically -->
|
|
380
|
+
<%- locals.reloadScript || '' %>
|
|
381
|
+
</body>
|
|
382
|
+
</html>
|
package/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const App = require('./lib/app');
|
|
2
|
+
|
|
3
|
+
// Export Next.js adapter
|
|
4
|
+
const NextJsAdapter = require('./lib/adapters/nextjs-adapter');
|
|
5
|
+
|
|
6
|
+
module.exports = {
|
|
7
|
+
App,
|
|
8
|
+
|
|
9
|
+
// Méthodes de création simplifiées
|
|
10
|
+
createApp: (options = {}) => new App(options),
|
|
11
|
+
|
|
12
|
+
// Démarrer en mode développement
|
|
13
|
+
startDev: (options = {}) => {
|
|
14
|
+
const app = new App({
|
|
15
|
+
...options,
|
|
16
|
+
isDev: true
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
app.loadRoutes();
|
|
20
|
+
return app.listen(options.port || 3000);
|
|
21
|
+
},
|
|
22
|
+
|
|
23
|
+
// Démarrer en mode production
|
|
24
|
+
start: (options = {}) => {
|
|
25
|
+
const app = new App({
|
|
26
|
+
...options,
|
|
27
|
+
isDev: false
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
app.loadRoutes();
|
|
31
|
+
return app.listen(options.port || 3000);
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// Next.js adapter
|
|
35
|
+
NextJsAdapter
|
|
36
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js Adapter for Veko.js
|
|
3
|
+
*
|
|
4
|
+
* Permet d'intégrer Veko.js avec Next.js pour bénéficier des deux frameworks
|
|
5
|
+
* - Utilisez les routes Veko.js dans Next.js
|
|
6
|
+
* - Utilisez les plugins Veko.js dans Next.js
|
|
7
|
+
* - Compatible avec les API routes de Next.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
class NextJsAdapter {
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
this.nextApp = options.nextApp;
|
|
15
|
+
this.enableVekoRoutes = options.enableVekoRoutes !== false;
|
|
16
|
+
this.enableVekoPlugins = options.enableVekoPlugins !== false;
|
|
17
|
+
this.routePrefix = options.routePrefix || '/api/veko';
|
|
18
|
+
this.vekoApp = null;
|
|
19
|
+
this.integrated = false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Intègre les routes Veko.js avec Next.js
|
|
24
|
+
* @param {App} vekoApp - Instance de l'application Veko
|
|
25
|
+
*/
|
|
26
|
+
integrateRoutes(vekoApp) {
|
|
27
|
+
if (!this.nextApp) {
|
|
28
|
+
throw new Error('Next.js app instance is required');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.vekoApp = vekoApp;
|
|
32
|
+
|
|
33
|
+
if (!this.enableVekoRoutes) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Créer un handler Next.js pour toutes les routes Veko
|
|
38
|
+
this.nextApp.use(this.routePrefix, (req, res, next) => {
|
|
39
|
+
// Passer la requête à Express (Veko utilise Express)
|
|
40
|
+
this.vekoApp.express(req, res, next);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
this.integrated = true;
|
|
44
|
+
this.vekoApp.log('success', 'Next.js adapter intégré', `Routes disponibles sous ${this.routePrefix}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Active les plugins Veko.js dans Next.js
|
|
49
|
+
* @param {App} vekoApp - Instance de l'application Veko
|
|
50
|
+
*/
|
|
51
|
+
usePlugins(vekoApp) {
|
|
52
|
+
if (!this.enableVekoPlugins) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.vekoApp = vekoApp;
|
|
57
|
+
|
|
58
|
+
// Exposer les plugins dans le contexte Next.js
|
|
59
|
+
if (this.nextApp.getRequestHandler) {
|
|
60
|
+
const originalHandler = this.nextApp.getRequestHandler();
|
|
61
|
+
|
|
62
|
+
this.nextApp.getRequestHandler = (req, res) => {
|
|
63
|
+
// Ajouter les plugins au contexte de la requête
|
|
64
|
+
req.vekoPlugins = vekoApp.pluginManager?.plugins || new Map();
|
|
65
|
+
req.vekoApp = vekoApp;
|
|
66
|
+
|
|
67
|
+
return originalHandler(req, res);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
vekoApp.log('info', 'Plugins Veko.js disponibles dans Next.js');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Crée un handler API Next.js à partir d'un handler Veko
|
|
76
|
+
* @param {Function} vekoHandler - Handler Veko.js
|
|
77
|
+
* @returns {Function} Handler compatible Next.js API route
|
|
78
|
+
*/
|
|
79
|
+
createApiHandler(vekoHandler) {
|
|
80
|
+
return async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
// Adapter le contexte pour être compatible avec Express
|
|
83
|
+
await new Promise((resolve, reject) => {
|
|
84
|
+
const next = (err) => {
|
|
85
|
+
if (err) reject(err);
|
|
86
|
+
else resolve();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Appeler le handler Veko avec le contexte Express
|
|
90
|
+
const result = vekoHandler(req, res, next);
|
|
91
|
+
|
|
92
|
+
// Si c'est une Promise, attendre sa résolution
|
|
93
|
+
if (result && typeof result.then === 'function') {
|
|
94
|
+
result.catch(reject);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
} catch (error) {
|
|
98
|
+
if (!res.headersSent) {
|
|
99
|
+
res.status(500).json({ error: error.message });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Middleware pour Next.js qui expose les fonctionnalités Veko
|
|
107
|
+
* @returns {Function} Middleware Express compatible
|
|
108
|
+
*/
|
|
109
|
+
middleware() {
|
|
110
|
+
return (req, res, next) => {
|
|
111
|
+
if (!this.vekoApp) {
|
|
112
|
+
return next();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Ajouter l'app Veko au contexte de la requête
|
|
116
|
+
req.vekoApp = this.vekoApp;
|
|
117
|
+
req.vekoPlugins = this.vekoApp.pluginManager?.plugins || new Map();
|
|
118
|
+
req.vekoLogger = this.vekoApp.logger;
|
|
119
|
+
|
|
120
|
+
// Exécuter les hooks Veko
|
|
121
|
+
if (this.vekoApp.pluginManager) {
|
|
122
|
+
this.vekoApp.pluginManager.executeHook('request:start', req, res);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Hook pour la fin de la requête
|
|
126
|
+
const originalEnd = res.end;
|
|
127
|
+
res.end = function(...args) {
|
|
128
|
+
if (this.vekoApp?.pluginManager) {
|
|
129
|
+
this.vekoApp.pluginManager.executeHook('request:end', req, res);
|
|
130
|
+
}
|
|
131
|
+
return originalEnd.apply(this, args);
|
|
132
|
+
}.bind({ vekoApp: this.vekoApp });
|
|
133
|
+
|
|
134
|
+
next();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Crée une route API Next.js dynamique depuis une route Veko
|
|
140
|
+
* @param {string} method - Méthode HTTP
|
|
141
|
+
* @param {string} path - Chemin de la route
|
|
142
|
+
* @param {Function|Array} handlers - Handlers Veko
|
|
143
|
+
*/
|
|
144
|
+
createNextApiRoute(method, path, handlers) {
|
|
145
|
+
if (!this.nextApp) {
|
|
146
|
+
throw new Error('Next.js app instance is required');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const fullPath = path.startsWith('/') ? path : `/${path}`;
|
|
150
|
+
const apiPath = `${this.routePrefix}${fullPath}`;
|
|
151
|
+
|
|
152
|
+
// Convertir les handlers en handler Next.js
|
|
153
|
+
const handlerArray = Array.isArray(handlers) ? handlers : [handlers];
|
|
154
|
+
const nextHandler = this.createApiHandler(async (req, res) => {
|
|
155
|
+
for (const handler of handlerArray) {
|
|
156
|
+
await new Promise((resolve, reject) => {
|
|
157
|
+
const next = (err) => {
|
|
158
|
+
if (err) reject(err);
|
|
159
|
+
else resolve();
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const result = handler(req, res, next);
|
|
163
|
+
if (result && typeof result.then === 'function') {
|
|
164
|
+
result.catch(reject);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Enregistrer la route dans Next.js
|
|
171
|
+
// Note: Cela nécessite une configuration spéciale dans Next.js
|
|
172
|
+
// car Next.js utilise un système de routing basé sur les fichiers
|
|
173
|
+
this.vekoApp?.log('info', `Route Next.js créée: ${method.toUpperCase()} ${apiPath}`);
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
path: apiPath,
|
|
177
|
+
method: method.toUpperCase(),
|
|
178
|
+
handler: nextHandler
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Génère les fichiers de routes API Next.js depuis les routes Veko
|
|
184
|
+
* @param {string} outputDir - Dossier de sortie (pages/api ou app/api)
|
|
185
|
+
*/
|
|
186
|
+
generateNextApiFiles(outputDir = 'pages/api') {
|
|
187
|
+
if (!this.vekoApp) {
|
|
188
|
+
throw new Error('Veko app must be integrated first');
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const routes = this.vekoApp.listRoutes();
|
|
192
|
+
const fs = require('fs');
|
|
193
|
+
const path = require('path');
|
|
194
|
+
|
|
195
|
+
routes.forEach(route => {
|
|
196
|
+
const routePath = route.path.replace(this.routePrefix, '').replace(/^\//, '');
|
|
197
|
+
const filePath = path.join(outputDir, routePath.replace(/\//g, '/'));
|
|
198
|
+
const dirPath = path.dirname(filePath);
|
|
199
|
+
|
|
200
|
+
// Créer le dossier si nécessaire
|
|
201
|
+
if (!fs.existsSync(dirPath)) {
|
|
202
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Générer le fichier API Next.js
|
|
206
|
+
const content = this.generateNextApiFileContent(route);
|
|
207
|
+
fs.writeFileSync(`${filePath}.js`, content);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
this.vekoApp.log('success', 'Fichiers API Next.js générés', `Dans ${outputDir}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Génère le contenu d'un fichier API Next.js
|
|
215
|
+
* @private
|
|
216
|
+
*/
|
|
217
|
+
generateNextApiFileContent(route) {
|
|
218
|
+
return `// Auto-generated by Veko.js Next.js Adapter
|
|
219
|
+
// Route: ${route.method} ${route.path}
|
|
220
|
+
|
|
221
|
+
export default async function handler(req, res) {
|
|
222
|
+
// Cette route est gérée par Veko.js
|
|
223
|
+
// Pour personnaliser, modifiez ce fichier
|
|
224
|
+
|
|
225
|
+
if (req.method !== '${route.method}') {
|
|
226
|
+
return res.status(405).json({ error: 'Method not allowed' });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Rediriger vers le handler Veko
|
|
230
|
+
// Note: Vous devez configurer le proxy ou utiliser le middleware
|
|
231
|
+
return res.status(200).json({
|
|
232
|
+
message: 'Route handled by Veko.js',
|
|
233
|
+
route: '${route.path}',
|
|
234
|
+
method: '${route.method}'
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = NextJsAdapter;
|