@teamvelix/velix 5.0.2 → 5.0.4

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/dist/index.js CHANGED
@@ -1,2899 +1,44 @@
1
- var __defProp = Object.defineProperty;
2
- var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __esm = (fn, res) => function __init() {
4
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
- };
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
-
11
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_post_49c9ba138cf4e7dd637766b727229610/node_modules/tsup/assets/esm_shims.js
12
- import path from "path";
13
- import { fileURLToPath } from "url";
14
- var init_esm_shims = __esm({
15
- "../../node_modules/.pnpm/tsup@8.5.1_jiti@1.21.7_post_49c9ba138cf4e7dd637766b727229610/node_modules/tsup/assets/esm_shims.js"() {
16
- "use strict";
17
- }
18
- });
19
-
20
- // server/image-optimizer.ts
21
- var image_optimizer_exports = {};
22
- __export(image_optimizer_exports, {
23
- handleImageOptimization: () => handleImageOptimization
24
- });
25
- import fs6 from "fs";
26
- import path7 from "path";
27
- async function handleImageOptimization(req, res, projectRoot) {
28
- let sharp;
29
- try {
30
- sharp = await import("sharp").then((m) => m.default || m);
31
- } catch (e) {
32
- }
33
- try {
34
- const url = new URL(req.url, `http://${req.headers.host || "localhost"}`);
35
- const imageUrl = url.searchParams.get("url");
36
- const widthStr = url.searchParams.get("w");
37
- const qualityStr = url.searchParams.get("q");
38
- if (!imageUrl) {
39
- res.writeHead(400);
40
- res.end("Missing url parameter");
41
- return;
42
- }
43
- const width = widthStr ? parseInt(widthStr, 10) : void 0;
44
- const quality = qualityStr ? parseInt(qualityStr, 10) : 75;
45
- let imageBuffer = null;
46
- if (imageUrl.startsWith("http")) {
47
- const response = await fetch(imageUrl);
48
- if (!response.ok) throw new Error(`Failed to fetch ${imageUrl}`);
49
- imageBuffer = Buffer.from(await response.arrayBuffer());
50
- } else {
51
- const publicDir = path7.join(projectRoot, "public");
52
- const resolvedPath = path7.join(publicDir, imageUrl.startsWith("/") ? imageUrl.slice(1) : imageUrl);
53
- if (!resolvedPath.startsWith(publicDir) || !fs6.existsSync(resolvedPath)) {
54
- res.writeHead(404);
55
- res.end("Image not found");
56
- return;
57
- }
58
- imageBuffer = fs6.readFileSync(resolvedPath);
59
- }
60
- if (!sharp) {
61
- res.writeHead(200, {
62
- "Content-Type": "image/jpeg",
63
- "Cache-Control": "public, max-age=31536000, immutable"
64
- });
65
- res.end(imageBuffer);
66
- return;
67
- }
68
- let processor = sharp(imageBuffer);
69
- if (width) {
70
- processor = processor.resize(width);
71
- }
72
- processor = processor.webp({ quality });
73
- const optimizedBuffer = await processor.toBuffer();
74
- res.writeHead(200, {
75
- "Content-Type": "image/webp",
76
- "Cache-Control": "public, max-age=31536000, immutable"
77
- });
78
- res.end(optimizedBuffer);
79
- } catch (error) {
80
- console.error("Image optimization error:", error);
81
- res.writeHead(500);
82
- res.end("Error processing image");
83
- }
84
- }
85
- var init_image_optimizer = __esm({
86
- "server/image-optimizer.ts"() {
87
- "use strict";
88
- init_esm_shims();
89
- }
90
- });
91
-
92
- // index.ts
93
- init_esm_shims();
94
-
95
- // config.ts
96
- init_esm_shims();
97
- import fs from "fs";
98
- import path2 from "path";
99
- import { pathToFileURL } from "url";
100
- import { z } from "zod";
101
- import pc from "picocolors";
102
- var AppConfigSchema = z.object({
103
- name: z.string().default("Velix App"),
104
- url: z.string().url().optional()
105
- }).default({});
106
- var ServerConfigSchema = z.object({
107
- port: z.number().min(1).max(65535).default(3e3),
108
- host: z.string().default("localhost")
109
- }).default({});
110
- var RoutingConfigSchema = z.object({
111
- trailingSlash: z.boolean().default(false)
112
- }).default({});
113
- var SEOConfigSchema = z.object({
114
- sitemap: z.boolean().default(true),
115
- robots: z.boolean().default(true),
116
- openGraph: z.boolean().default(true)
117
- }).default({});
118
- var BuildConfigSchema = z.object({
119
- target: z.string().default("es2022"),
120
- minify: z.boolean().default(true),
121
- sourcemap: z.boolean().default(true),
122
- splitting: z.boolean().default(true),
123
- outDir: z.string().default(".velix")
124
- }).default({});
125
- var ExperimentalConfigSchema = z.object({
126
- islands: z.boolean().default(true),
127
- streaming: z.boolean().default(true)
128
- }).default({});
129
- var PluginSchema = z.union([
130
- z.string(),
131
- z.object({
132
- name: z.string()
133
- }).passthrough()
134
- ]);
135
- var VelixConfigSchema = z.object({
136
- // App identity
137
- app: AppConfigSchema,
138
- // DevTools toggle
139
- devtools: z.boolean().default(true),
140
- // Server options
141
- server: ServerConfigSchema,
142
- // Routing options
143
- routing: RoutingConfigSchema,
144
- // SEO configuration
145
- seo: SEOConfigSchema,
146
- // Build options
147
- build: BuildConfigSchema,
148
- // Experimental features
149
- experimental: ExperimentalConfigSchema,
150
- // Plugins
151
- plugins: z.array(PluginSchema).default([]),
152
- // Directories (resolved automatically)
153
- appDir: z.string().default("app"),
154
- publicDir: z.string().default("public"),
155
- // Stylesheets
156
- styles: z.array(z.string()).default([]),
157
- // Favicon
158
- favicon: z.string().nullable().default(null)
159
- });
160
- var defaultConfig = VelixConfigSchema.parse({});
161
- function defineConfig(config) {
162
- return config;
163
- }
164
- async function loadConfig(projectRoot) {
165
- const configPathTs = path2.join(projectRoot, "velix.config.ts");
166
- const configPathJs = path2.join(projectRoot, "velix.config.js");
167
- const configPathLegacyTs = path2.join(projectRoot, "flexireact.config.ts");
168
- const configPathLegacyJs = path2.join(projectRoot, "flexireact.config.js");
169
- let configPath = null;
170
- if (fs.existsSync(configPathTs)) configPath = configPathTs;
171
- else if (fs.existsSync(configPathJs)) configPath = configPathJs;
172
- else if (fs.existsSync(configPathLegacyTs)) configPath = configPathLegacyTs;
173
- else if (fs.existsSync(configPathLegacyJs)) configPath = configPathLegacyJs;
174
- let userConfig = {};
175
- if (configPath) {
176
- try {
177
- const configUrl = pathToFileURL(configPath).href;
178
- const module = await import(`${configUrl}?t=${Date.now()}`);
179
- userConfig = module.default || module;
180
- } catch (error) {
181
- console.warn(pc.yellow(`\u26A0 Failed to load config: ${error.message}`));
182
- }
183
- }
184
- const merged = deepMerge(defaultConfig, userConfig);
185
- try {
186
- return VelixConfigSchema.parse(merged);
187
- } catch (err) {
188
- if (err instanceof z.ZodError) {
189
- console.error(pc.red("\u2716 Configuration validation failed:"));
190
- for (const issue of err.issues) {
191
- console.error(pc.dim(` - ${issue.path.join(".")}: ${issue.message}`));
192
- }
193
- process.exit(1);
194
- }
195
- throw err;
196
- }
197
- }
198
- function resolvePaths(config, projectRoot) {
199
- return {
200
- ...config,
201
- resolvedAppDir: path2.resolve(projectRoot, config.appDir),
202
- resolvedPublicDir: path2.resolve(projectRoot, config.publicDir),
203
- resolvedOutDir: path2.resolve(projectRoot, config.build.outDir)
204
- };
205
- }
206
- function deepMerge(target, source) {
207
- const result = { ...target };
208
- for (const key in source) {
209
- if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
210
- result[key] = deepMerge(target[key] || {}, source[key]);
211
- } else {
212
- result[key] = source[key];
213
- }
214
- }
215
- return result;
216
- }
217
-
218
- // helpers.ts
219
- init_esm_shims();
220
- var RedirectError = class extends Error {
221
- url;
222
- statusCode;
223
- type = "redirect";
224
- constructor(url, statusCode = 307) {
225
- super(`Redirect to ${url}`);
226
- this.name = "RedirectError";
227
- this.url = url;
228
- this.statusCode = statusCode;
229
- }
230
- };
231
- var NotFoundError = class extends Error {
232
- type = "notFound";
233
- constructor(message = "Page not found") {
234
- super(message);
235
- this.name = "NotFoundError";
236
- }
237
- };
238
- function redirect(url, type = "replace") {
239
- const statusCode = type === "permanent" ? 308 : 307;
240
- throw new RedirectError(url, statusCode);
241
- }
242
- function notFound(message) {
243
- throw new NotFoundError(message);
244
- }
245
- function json(data, options = {}) {
246
- const { status = 200, headers: headers2 = {} } = options;
247
- return new Response(JSON.stringify(data), {
248
- status,
249
- headers: { "Content-Type": "application/json", ...headers2 }
250
- });
251
- }
252
- function html(content, options = {}) {
253
- const { status = 200, headers: headers2 = {} } = options;
254
- return new Response(content, {
255
- status,
256
- headers: { "Content-Type": "text/html; charset=utf-8", ...headers2 }
257
- });
258
- }
259
- function text(content, options = {}) {
260
- const { status = 200, headers: headers2 = {} } = options;
261
- return new Response(content, {
262
- status,
263
- headers: { "Content-Type": "text/plain; charset=utf-8", ...headers2 }
264
- });
265
- }
266
- var cookies = {
267
- parse(cookieHeader) {
268
- const cookies2 = {};
269
- if (!cookieHeader) return cookies2;
270
- cookieHeader.split(";").forEach((cookie) => {
271
- const [name, ...rest] = cookie.split("=");
272
- if (name) {
273
- cookies2[name.trim()] = decodeURIComponent(rest.join("=").trim());
274
- }
275
- });
276
- return cookies2;
277
- },
278
- get(request, name) {
279
- const cookieHeader = request.headers.get("cookie") || "";
280
- return this.parse(cookieHeader)[name];
281
- },
282
- getAll(request) {
283
- const cookieHeader = request.headers.get("cookie") || "";
284
- return this.parse(cookieHeader);
285
- },
286
- serialize(name, value, options = {}) {
287
- let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
288
- if (options.maxAge !== void 0) cookie += `; Max-Age=${options.maxAge}`;
289
- if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
290
- if (options.path) cookie += `; Path=${options.path}`;
291
- if (options.domain) cookie += `; Domain=${options.domain}`;
292
- if (options.secure) cookie += "; Secure";
293
- if (options.httpOnly) cookie += "; HttpOnly";
294
- if (options.sameSite) {
295
- cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`;
296
- }
297
- return cookie;
298
- },
299
- set(name, value, options = {}) {
300
- return this.serialize(name, value, {
301
- path: "/",
302
- httpOnly: true,
303
- secure: process.env.NODE_ENV === "production",
304
- sameSite: "lax",
305
- ...options
306
- });
307
- },
308
- delete(name, options = {}) {
309
- return this.serialize(name, "", { ...options, path: "/", maxAge: 0 });
310
- }
311
- };
312
- var headers = {
313
- create(init) {
314
- return new Headers(init);
315
- },
316
- get(request, name) {
317
- return request.headers.get(name);
318
- },
319
- getAll(request) {
320
- const result = {};
321
- request.headers.forEach((value, key) => {
322
- result[key] = value;
323
- });
324
- return result;
325
- },
326
- has(request, name) {
327
- return request.headers.has(name);
328
- },
329
- contentType(request) {
330
- return request.headers.get("content-type");
331
- },
332
- acceptsJson(request) {
333
- const accept = request.headers.get("accept") || "";
334
- return accept.includes("application/json") || accept.includes("*/*");
335
- },
336
- isAjax(request) {
337
- return request.headers.get("x-requested-with") === "XMLHttpRequest" || this.acceptsJson(request);
338
- },
339
- authorization(request) {
340
- const auth = request.headers.get("authorization");
341
- if (!auth) return null;
342
- const [type, ...rest] = auth.split(" ");
343
- return { type: type.toLowerCase(), credentials: rest.join(" ") };
344
- },
345
- bearerToken(request) {
346
- const auth = this.authorization(request);
347
- return auth?.type === "bearer" ? auth.credentials : null;
348
- },
349
- security() {
350
- return {
351
- "X-Content-Type-Options": "nosniff",
352
- "X-Frame-Options": "DENY",
353
- "X-XSS-Protection": "1; mode=block",
354
- "Referrer-Policy": "strict-origin-when-cross-origin",
355
- "Permissions-Policy": "camera=(), microphone=(), geolocation=()"
356
- };
357
- },
358
- cors(options = {}) {
359
- const {
360
- origin = "*",
361
- methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
362
- headers: allowHeaders = ["Content-Type", "Authorization"],
363
- credentials = false,
364
- maxAge = 86400
365
- } = options;
366
- const corsHeaders = {
367
- "Access-Control-Allow-Origin": origin,
368
- "Access-Control-Allow-Methods": methods.join(", "),
369
- "Access-Control-Allow-Headers": allowHeaders.join(", "),
370
- "Access-Control-Max-Age": String(maxAge)
371
- };
372
- if (credentials) corsHeaders["Access-Control-Allow-Credentials"] = "true";
373
- return corsHeaders;
374
- },
375
- cache(options = {}) {
376
- if (options.noStore) {
377
- return { "Cache-Control": "no-store, no-cache, must-revalidate" };
378
- }
379
- const directives = [];
380
- directives.push(options.private ? "private" : "public");
381
- if (options.maxAge !== void 0) directives.push(`max-age=${options.maxAge}`);
382
- if (options.sMaxAge !== void 0) directives.push(`s-maxage=${options.sMaxAge}`);
383
- if (options.staleWhileRevalidate !== void 0) directives.push(`stale-while-revalidate=${options.staleWhileRevalidate}`);
384
- return { "Cache-Control": directives.join(", ") };
385
- }
386
- };
387
- async function parseJson(request) {
388
- try {
389
- return await request.json();
390
- } catch {
391
- throw new Error("Invalid JSON body");
392
- }
393
- }
394
- async function parseFormData(request) {
395
- return await request.formData();
396
- }
397
- function parseSearchParams(request) {
398
- return new URL(request.url).searchParams;
399
- }
400
- function getMethod(request) {
401
- return request.method.toUpperCase();
402
- }
403
- function getPathname(request) {
404
- return new URL(request.url).pathname;
405
- }
406
- function isMethod(request, method) {
407
- const reqMethod = getMethod(request);
408
- return Array.isArray(method) ? method.map((m) => m.toUpperCase()).includes(reqMethod) : reqMethod === method.toUpperCase();
409
- }
410
-
411
- // router/index.ts
412
- init_esm_shims();
413
- import fs3 from "fs";
414
- import path4 from "path";
415
-
416
- // utils.ts
417
- init_esm_shims();
418
- import fs2 from "fs";
419
- import path3 from "path";
420
- import crypto from "crypto";
421
- function generateHash(content) {
422
- return crypto.createHash("md5").update(content).digest("hex").slice(0, 8);
423
- }
424
- function escapeHtml(str) {
425
- const htmlEntities = {
426
- "&": "&",
427
- "<": "&lt;",
428
- ">": "&gt;",
429
- '"': "&quot;",
430
- "'": "&#39;"
431
- };
432
- return String(str).replace(/[&<>"']/g, (char) => htmlEntities[char]);
433
- }
434
- function findFiles(dir, pattern, files = []) {
435
- if (!fs2.existsSync(dir)) return files;
436
- const entries = fs2.readdirSync(dir, { withFileTypes: true });
437
- for (const entry of entries) {
438
- const fullPath = path3.join(dir, entry.name);
439
- if (entry.isDirectory()) findFiles(fullPath, pattern, files);
440
- else if (pattern.test(entry.name)) files.push(fullPath);
441
- }
442
- return files;
443
- }
444
- function ensureDir(dir) {
445
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
446
- }
447
- function cleanDir(dir) {
448
- if (fs2.existsSync(dir)) fs2.rmSync(dir, { recursive: true, force: true });
449
- fs2.mkdirSync(dir, { recursive: true });
450
- }
451
- function copyDir(src, dest) {
452
- ensureDir(dest);
453
- const entries = fs2.readdirSync(src, { withFileTypes: true });
454
- for (const entry of entries) {
455
- const srcPath = path3.join(src, entry.name);
456
- const destPath = path3.join(dest, entry.name);
457
- if (entry.isDirectory()) copyDir(srcPath, destPath);
458
- else fs2.copyFileSync(srcPath, destPath);
459
- }
460
- }
461
- function debounce(fn, delay) {
462
- let timeout;
463
- return (...args) => {
464
- clearTimeout(timeout);
465
- timeout = setTimeout(() => fn(...args), delay);
466
- };
467
- }
468
- function formatBytes(bytes) {
469
- if (bytes === 0) return "0 B";
470
- const k = 1024;
471
- const sizes = ["B", "KB", "MB", "GB"];
472
- const i = Math.floor(Math.log(bytes) / Math.log(k));
473
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
474
- }
475
- function formatTime(ms) {
476
- if (ms < 1e3) return `${ms}ms`;
477
- return `${(ms / 1e3).toFixed(2)}s`;
478
- }
479
- function sleep(ms) {
480
- return new Promise((resolve) => setTimeout(resolve, ms));
481
- }
482
- function isServerComponent(filePath) {
483
- try {
484
- const content = fs2.readFileSync(filePath, "utf-8");
485
- const firstLine = content.split("\n")[0].trim();
486
- return firstLine === "'use server'" || firstLine === '"use server"';
487
- } catch {
488
- return false;
489
- }
490
- }
491
- function isClientComponent(filePath) {
492
- try {
493
- const content = fs2.readFileSync(filePath, "utf-8");
494
- const firstLine = content.split("\n")[0].trim();
495
- return firstLine === "'use client'" || firstLine === '"use client"';
496
- } catch {
497
- return false;
498
- }
499
- }
500
- function isIsland(filePath) {
501
- try {
502
- const content = fs2.readFileSync(filePath, "utf-8");
503
- const firstLine = content.split("\n")[0].trim();
504
- return firstLine === "'use island'" || firstLine === '"use island"';
505
- } catch {
506
- return false;
507
- }
508
- }
509
-
510
- // router/index.ts
511
- var RouteType = {
512
- PAGE: "page",
513
- API: "api",
514
- LAYOUT: "layout",
515
- LOADING: "loading",
516
- ERROR: "error",
517
- NOT_FOUND: "not-found"
518
- };
519
- function buildRouteTree(appDir) {
520
- const projectRoot = path4.dirname(appDir);
521
- const routes = {
522
- pages: [],
523
- api: [],
524
- layouts: /* @__PURE__ */ new Map(),
525
- tree: {},
526
- appRoutes: []
527
- };
528
- if (fs3.existsSync(appDir)) {
529
- scanAppDirectory(appDir, appDir, routes);
530
- }
531
- const serverApiDir = path4.join(projectRoot, "server", "api");
532
- if (fs3.existsSync(serverApiDir)) {
533
- scanApiDirectory(serverApiDir, serverApiDir, routes);
534
- }
535
- const rootLayoutTsx = path4.join(appDir, "layout.tsx");
536
- const rootLayoutJsx = path4.join(appDir, "layout.jsx");
537
- if (fs3.existsSync(rootLayoutTsx)) routes.rootLayout = rootLayoutTsx;
538
- else if (fs3.existsSync(rootLayoutJsx)) routes.rootLayout = rootLayoutJsx;
539
- routes.tree = buildTree(routes.appRoutes);
540
- return routes;
541
- }
542
- function scanAppDirectory(baseDir, currentDir, routes, parentSegments = [], parentLayout = null, parentMiddleware = null) {
543
- const entries = fs3.readdirSync(currentDir, { withFileTypes: true });
544
- const specialFiles = {
545
- page: null,
546
- layout: null,
547
- loading: null,
548
- error: null,
549
- notFound: null,
550
- template: null,
551
- middleware: null
552
- };
553
- for (const entry of entries) {
554
- if (entry.isFile()) {
555
- const name = entry.name.replace(/\.(jsx|js|tsx|ts)$/, "");
556
- const fullPath = path4.join(currentDir, entry.name);
557
- const ext = path4.extname(entry.name);
558
- if (![".tsx", ".jsx", ".ts", ".js"].includes(ext)) continue;
559
- if (name === "page") specialFiles.page = fullPath;
560
- if (name === "layout") specialFiles.layout = fullPath;
561
- if (name === "loading") specialFiles.loading = fullPath;
562
- if (name === "error") specialFiles.error = fullPath;
563
- if (name === "not-found") specialFiles.notFound = fullPath;
564
- if (name === "template") specialFiles.template = fullPath;
565
- if (name === "middleware" || name === "_middleware") specialFiles.middleware = fullPath;
566
- if (name.startsWith("[") && name.endsWith("]") && [".tsx", ".jsx"].includes(ext)) {
567
- const paramName = name.slice(1, -1);
568
- let segmentName;
569
- if (paramName.startsWith("...")) {
570
- segmentName = "*" + paramName.slice(3);
571
- } else {
572
- segmentName = ":" + paramName;
573
- }
574
- const routePath = "/" + [...parentSegments, segmentName].join("/");
575
- routes.appRoutes.push({
576
- type: RouteType.PAGE,
577
- path: routePath.replace(/\/+/g, "/"),
578
- filePath: fullPath,
579
- pattern: createRoutePattern(routePath),
580
- segments: [...parentSegments, segmentName],
581
- layout: specialFiles.layout || parentLayout,
582
- loading: specialFiles.loading,
583
- error: specialFiles.error,
584
- notFound: specialFiles.notFound,
585
- template: specialFiles.template,
586
- middleware: specialFiles.middleware || parentMiddleware,
587
- isServerComponent: isServerComponent(fullPath),
588
- isClientComponent: isClientComponent(fullPath),
589
- isIsland: isIsland(fullPath)
590
- });
591
- }
592
- }
593
- }
594
- if (specialFiles.page) {
595
- const routePath = "/" + parentSegments.join("/") || "/";
596
- routes.appRoutes.push({
597
- type: RouteType.PAGE,
598
- path: routePath.replace(/\/+/g, "/") || "/",
599
- filePath: specialFiles.page,
600
- pattern: createRoutePattern(routePath || "/"),
601
- segments: parentSegments,
602
- layout: specialFiles.layout || parentLayout,
603
- loading: specialFiles.loading,
604
- error: specialFiles.error,
605
- notFound: specialFiles.notFound,
606
- template: specialFiles.template,
607
- middleware: specialFiles.middleware || parentMiddleware,
608
- isServerComponent: isServerComponent(specialFiles.page),
609
- isClientComponent: isClientComponent(specialFiles.page),
610
- isIsland: isIsland(specialFiles.page)
611
- });
612
- }
613
- for (const entry of entries) {
614
- if (entry.isDirectory()) {
615
- const fullPath = path4.join(currentDir, entry.name);
616
- if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
617
- const isGroup = entry.name.startsWith("(") && entry.name.endsWith(")");
618
- let segmentName = entry.name;
619
- if (entry.name.startsWith("[") && entry.name.endsWith("]")) {
620
- segmentName = ":" + entry.name.slice(1, -1);
621
- if (entry.name.startsWith("[...")) {
622
- segmentName = "*" + entry.name.slice(4, -1);
623
- }
624
- if (entry.name.startsWith("[[...")) {
625
- segmentName = "*" + entry.name.slice(5, -2);
626
- }
627
- }
628
- const newSegments = isGroup ? parentSegments : [...parentSegments, segmentName];
629
- const newLayout = specialFiles.layout || parentLayout;
630
- const newMiddleware = specialFiles.middleware || parentMiddleware;
631
- scanAppDirectory(baseDir, fullPath, routes, newSegments, newLayout, newMiddleware);
632
- }
633
- }
634
- }
635
- function scanApiDirectory(baseDir, currentDir, routes, parentSegments = []) {
636
- const entries = fs3.readdirSync(currentDir, { withFileTypes: true });
637
- for (const entry of entries) {
638
- const fullPath = path4.join(currentDir, entry.name);
639
- if (entry.isDirectory()) {
640
- scanApiDirectory(baseDir, fullPath, routes, [...parentSegments, entry.name]);
641
- } else if (entry.isFile()) {
642
- const ext = path4.extname(entry.name);
643
- if (![".ts", ".js"].includes(ext)) continue;
644
- const baseName = path4.basename(entry.name, ext);
645
- const apiSegments = baseName === "route" || baseName === "index" ? parentSegments : [...parentSegments, baseName];
646
- const apiPath = "/api/" + apiSegments.join("/");
647
- routes.api.push({
648
- type: RouteType.API,
649
- path: apiPath.replace(/\/+/g, "/") || "/api",
650
- filePath: fullPath,
651
- pattern: createRoutePattern(apiPath),
652
- segments: ["api", ...apiSegments].filter(Boolean)
653
- });
654
- }
655
- }
656
- }
657
- function createRoutePattern(routePath) {
658
- let pattern = routePath.replace(/\*[^/]*/g, "(.*)").replace(/:[^/]+/g, "([^/]+)").replace(/\//g, "\\/");
659
- return new RegExp(`^${pattern}$`);
660
- }
661
- function matchRoute(urlPath, routes) {
662
- const normalizedPath = urlPath === "" ? "/" : urlPath.split("?")[0];
663
- for (const route of routes) {
664
- const match = normalizedPath.match(route.pattern);
665
- if (match) {
666
- const params = extractParams(route.path, match);
667
- return { ...route, params };
668
- }
669
- }
670
- return null;
671
- }
672
- function extractParams(routePath, match) {
673
- const params = {};
674
- const paramNames = [];
675
- const paramRegex = /:([^/]+)|\*([^/]*)/g;
676
- let paramMatch;
677
- while ((paramMatch = paramRegex.exec(routePath)) !== null) {
678
- paramNames.push(paramMatch[1] || paramMatch[2] || "splat");
679
- }
680
- paramNames.forEach((name, index) => {
681
- params[name] = match[index + 1];
682
- });
683
- return params;
684
- }
685
- function findRouteLayouts(route, layoutsMap) {
686
- const layouts = [];
687
- for (const segment of route.segments) {
688
- if (layoutsMap.has(segment)) {
689
- layouts.push({ name: segment, filePath: layoutsMap.get(segment) });
690
- }
691
- }
692
- if (route.layout) {
693
- layouts.push({ name: "route", filePath: route.layout });
694
- }
695
- if (layoutsMap.has("root")) {
696
- layouts.unshift({ name: "root", filePath: layoutsMap.get("root") });
697
- }
698
- return layouts;
699
- }
700
- function buildTree(routes) {
701
- const tree = { children: {}, routes: [] };
702
- for (const route of routes) {
703
- let current = tree;
704
- for (const segment of route.segments) {
705
- if (!current.children[segment]) {
706
- current.children[segment] = { children: {}, routes: [] };
707
- }
708
- current = current.children[segment];
709
- }
710
- current.routes.push(route);
711
- }
712
- return tree;
713
- }
714
-
715
- // server/index.ts
716
- init_esm_shims();
717
- import http from "http";
718
- import fs7 from "fs";
719
- import path8 from "path";
720
- import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL3 } from "url";
721
- import React2 from "react";
722
- import { renderToString } from "react-dom/server";
723
-
724
- // middleware/index.ts
725
- init_esm_shims();
726
- import fs4 from "fs";
727
- import path5 from "path";
728
- var middlewares = {
729
- cors(options = {}) {
730
- const {
731
- origin = "*",
732
- methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
733
- headers: allowHeaders = ["Content-Type", "Authorization"],
734
- credentials = false,
735
- maxAge = 86400
736
- } = options;
737
- return async (req, res, next) => {
738
- const originHeader = typeof origin === "string" ? origin : origin.includes(req.headers.origin) ? req.headers.origin : "";
739
- res.header("Access-Control-Allow-Origin", originHeader);
740
- res.header("Access-Control-Allow-Methods", methods.join(", "));
741
- res.header("Access-Control-Allow-Headers", allowHeaders.join(", "));
742
- res.header("Access-Control-Max-Age", String(maxAge));
743
- if (credentials) {
744
- res.header("Access-Control-Allow-Credentials", "true");
745
- }
746
- if (req.method === "OPTIONS") {
747
- res.status(204);
748
- return;
749
- }
750
- await next();
751
- };
752
- },
753
- rateLimit(options = {}) {
754
- const { windowMs = 6e4, max = 100, message = "Too many requests" } = options;
755
- const store = /* @__PURE__ */ new Map();
756
- return async (req, res, next) => {
757
- const ip = req.headers["x-forwarded-for"] || "unknown";
758
- const now = Date.now();
759
- const record = store.get(ip);
760
- if (!record || now > record.resetTime) {
761
- store.set(ip, { count: 1, resetTime: now + windowMs });
762
- } else if (record.count >= max) {
763
- res.status(429).json({ error: message });
764
- return;
765
- } else {
766
- record.count++;
767
- }
768
- await next();
769
- };
770
- },
771
- security() {
772
- return async (_req, res, next) => {
773
- res.header("X-Content-Type-Options", "nosniff");
774
- res.header("X-Frame-Options", "DENY");
775
- res.header("X-XSS-Protection", "1; mode=block");
776
- res.header("Referrer-Policy", "strict-origin-when-cross-origin");
777
- await next();
778
- };
779
- }
780
- };
781
- async function loadMiddleware(projectRoot) {
782
- const middlewareDir = path5.join(projectRoot, "middleware");
783
- const fns = [];
784
- if (!fs4.existsSync(middlewareDir)) return fns;
785
- const files = fs4.readdirSync(middlewareDir).filter((f) => /\.(ts|js)$/.test(f)).sort();
786
- for (const file of files) {
787
- try {
788
- const filePath = path5.join(middlewareDir, file);
789
- const { pathToFileURL: pathToFileURL4 } = await import("url");
790
- const url = pathToFileURL4(filePath).href;
791
- const mod = await import(`${url}?t=${Date.now()}`);
792
- const fn = mod.default || mod.middleware;
793
- if (typeof fn === "function") fns.push(fn);
794
- } catch (err) {
795
- console.warn(`\u26A0 Failed to load middleware ${file}: ${err.message}`);
796
- }
797
- }
798
- return fns;
799
- }
800
- async function runMiddleware(req, res, fns) {
801
- const result = { continue: true, rewritten: false };
802
- if (fns.length === 0) return result;
803
- const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
804
- const cookies2 = {};
805
- const cookieHeader = req.headers.cookie;
806
- if (cookieHeader) {
807
- cookieHeader.split(";").forEach((c2) => {
808
- const [k, ...v] = c2.split("=");
809
- if (k) cookies2[k.trim()] = v.join("=").trim();
810
- });
811
- }
812
- const mReq = {
813
- url: req.url || "/",
814
- method: req.method || "GET",
815
- headers: req.headers,
816
- cookies: cookies2,
817
- params: {},
818
- query: Object.fromEntries(url.searchParams),
819
- raw: req
820
- };
821
- let ended = false;
822
- const mRes = {
823
- _statusCode: 200,
824
- _headers: {},
825
- _redirectUrl: null,
826
- _rewriteUrl: null,
827
- _ended: false,
828
- status(code) {
829
- this._statusCode = code;
830
- return this;
831
- },
832
- header(name, value) {
833
- this._headers[name] = value;
834
- return this;
835
- },
836
- json(data) {
837
- this._headers["Content-Type"] = "application/json";
838
- res.writeHead(this._statusCode, this._headers);
839
- res.end(JSON.stringify(data));
840
- this._ended = true;
841
- ended = true;
842
- },
843
- redirect(url2, status = 307) {
844
- this._redirectUrl = url2;
845
- this._statusCode = status;
846
- res.writeHead(status, { Location: url2, ...this._headers });
847
- res.end();
848
- this._ended = true;
849
- ended = true;
850
- },
851
- rewrite(url2) {
852
- this._rewriteUrl = url2;
853
- req.url = url2;
854
- result.rewritten = true;
855
- },
856
- async next() {
857
- }
858
- };
859
- let index = 0;
860
- const next = async () => {
861
- if (ended || index >= fns.length) return;
862
- const fn = fns[index++];
863
- await fn(mReq, mRes, next);
864
- };
865
- await next();
866
- if (!ended) {
867
- for (const [key, value] of Object.entries(mRes._headers)) {
868
- res.setHeader(key, value);
869
- }
870
- }
871
- result.continue = !ended;
872
- return result;
873
- }
874
- function composeMiddleware(...fns) {
875
- return async (req, res, next) => {
876
- let index = 0;
877
- const run = async () => {
878
- if (index >= fns.length) {
879
- await next();
880
- return;
881
- }
882
- const fn = fns[index++];
883
- await fn(req, res, run);
884
- };
885
- await run();
886
- };
887
- }
888
-
889
- // plugins/index.ts
890
- init_esm_shims();
891
- import fs5 from "fs";
892
- import path6 from "path";
893
- import { pathToFileURL as pathToFileURL2 } from "url";
894
- var PluginHooks = {
895
- CONFIG: "config",
896
- SERVER_START: "server:start",
897
- REQUEST: "request",
898
- RESPONSE: "response",
899
- ROUTES_LOADED: "routes:loaded",
900
- BEFORE_RENDER: "render:before",
901
- AFTER_RENDER: "render:after",
902
- BUILD_START: "build:start",
903
- BUILD_END: "build:end"
904
- };
905
- var PluginManager = class {
906
- plugins = [];
907
- hooks = /* @__PURE__ */ new Map();
908
- /**
909
- * Register a plugin
910
- */
911
- register(plugin) {
912
- this.plugins.push(plugin);
913
- if (plugin.hooks) {
914
- for (const [hookName, handler] of Object.entries(plugin.hooks)) {
915
- if (handler) {
916
- const existing = this.hooks.get(hookName) || [];
917
- existing.push(handler);
918
- this.hooks.set(hookName, existing);
919
- }
920
- }
921
- }
922
- }
923
- /**
924
- * Run a hook with arguments
925
- */
926
- async runHook(hookName, ...args) {
927
- const handlers = this.hooks.get(hookName) || [];
928
- for (const handler of handlers) {
929
- await handler(...args);
930
- }
931
- }
932
- /**
933
- * Run a waterfall hook — each handler transforms the first argument
934
- */
935
- async runWaterfallHook(hookName, value, ...args) {
936
- const handlers = this.hooks.get(hookName) || [];
937
- let result = value;
938
- for (const handler of handlers) {
939
- const transformed = await handler(result, ...args);
940
- if (transformed !== void 0) result = transformed;
941
- }
942
- return result;
943
- }
944
- /**
945
- * Get all registered plugins
946
- */
947
- getPlugins() {
948
- return [...this.plugins];
949
- }
950
- /**
951
- * Check if a plugin is registered
952
- */
953
- hasPlugin(name) {
954
- return this.plugins.some((p) => p.name === name);
955
- }
956
- };
957
- var pluginManager = new PluginManager();
958
- async function loadPlugins(projectRoot, config) {
959
- const pluginEntries = config.plugins || [];
960
- for (const entry of pluginEntries) {
961
- try {
962
- if (typeof entry === "string") {
963
- const localPath = path6.join(projectRoot, "plugins", entry + ".ts");
964
- const localPathJs = path6.join(projectRoot, "plugins", entry + ".js");
965
- const localPathDir = path6.join(projectRoot, "plugins", entry, "index.ts");
966
- let pluginPath = null;
967
- if (fs5.existsSync(localPath)) pluginPath = localPath;
968
- else if (fs5.existsSync(localPathJs)) pluginPath = localPathJs;
969
- else if (fs5.existsSync(localPathDir)) pluginPath = localPathDir;
970
- if (pluginPath) {
971
- const url = pathToFileURL2(pluginPath).href;
972
- const mod = await import(`${url}?t=${Date.now()}`);
973
- const plugin = mod.default || mod;
974
- if (plugin.name) {
975
- pluginManager.register(plugin);
976
- }
977
- } else {
978
- try {
979
- const mod = await import(entry);
980
- const plugin = mod.default || mod;
981
- if (plugin.name) pluginManager.register(plugin);
982
- } catch {
983
- console.warn(`\u26A0 Plugin not found: ${entry}`);
984
- }
985
- }
986
- } else if (typeof entry === "object" && entry.name) {
987
- pluginManager.register(entry);
988
- }
989
- } catch (err) {
990
- console.warn(`\u26A0 Failed to load plugin: ${err.message}`);
991
- }
992
- }
993
- }
994
- function definePlugin(definition) {
995
- return definition;
996
- }
997
- var builtinPlugins = {
998
- /**
999
- * Security headers plugin
1000
- */
1001
- security: definePlugin({
1002
- name: "velix:security",
1003
- hooks: {
1004
- [PluginHooks.RESPONSE]: (_req, res) => {
1005
- if (!res.headersSent) {
1006
- res.setHeader("X-Content-Type-Options", "nosniff");
1007
- res.setHeader("X-Frame-Options", "DENY");
1008
- res.setHeader("X-XSS-Protection", "1; mode=block");
1009
- }
1010
- }
1011
- }
1012
- }),
1013
- /**
1014
- * Request logging plugin
1015
- */
1016
- logger: definePlugin({
1017
- name: "velix:logger",
1018
- hooks: {
1019
- [PluginHooks.RESPONSE]: (req, _res, duration) => {
1020
- console.log(` ${req.method} ${req.url} ${duration}ms`);
1021
- }
1022
- }
1023
- })
1024
- };
1025
-
1026
- // plugins/tailwind.ts
1027
- init_esm_shims();
1028
- import { spawnSync, spawn } from "child_process";
1029
-
1030
- // logger.ts
1031
- init_esm_shims();
1032
- var colors = {
1033
- reset: "\x1B[0m",
1034
- bold: "\x1B[1m",
1035
- dim: "\x1B[2m",
1036
- red: "\x1B[31m",
1037
- green: "\x1B[32m",
1038
- yellow: "\x1B[33m",
1039
- blue: "\x1B[34m",
1040
- magenta: "\x1B[35m",
1041
- cyan: "\x1B[36m",
1042
- white: "\x1B[37m",
1043
- gray: "\x1B[90m"
1044
- };
1045
- var c = colors;
1046
- function getStatusColor(status) {
1047
- if (status >= 500) return c.red;
1048
- if (status >= 400) return c.yellow;
1049
- if (status >= 300) return c.cyan;
1050
- if (status >= 200) return c.green;
1051
- return c.white;
1052
- }
1053
- function fmtTime(ms) {
1054
- if (ms < 1) return `${c.gray}<1ms${c.reset}`;
1055
- if (ms < 100) return `${c.green}${ms}ms${c.reset}`;
1056
- if (ms < 500) return `${c.yellow}${ms}ms${c.reset}`;
1057
- return `${c.red}${ms}ms${c.reset}`;
1058
- }
1059
- var VERSION = "5.0.0";
1060
- var LOGO = `
1061
- ${c.cyan}\u25B2${c.reset} ${c.bold}Velix${c.reset} ${c.dim}v${VERSION}${c.reset}
1062
- `;
1063
- var logger = {
1064
- logo() {
1065
- console.log(LOGO);
1066
- console.log(`${c.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
1067
- console.log("");
1068
- },
1069
- serverStart(config, startTime = Date.now()) {
1070
- const { port, host, mode, pagesDir } = config;
1071
- const elapsed = Date.now() - startTime;
1072
- console.log(LOGO);
1073
- console.log(` ${c.green}\u2714${c.reset} ${c.bold}Ready${c.reset} in ${elapsed}ms`);
1074
- console.log("");
1075
- console.log(` ${c.bold}Local:${c.reset} ${c.cyan}http://${host}:${port}${c.reset}`);
1076
- console.log(` ${c.bold}Mode:${c.reset} ${mode === "development" ? c.yellow : c.green}${mode}${c.reset}`);
1077
- if (pagesDir) console.log(` ${c.bold}App:${c.reset} ${c.dim}${pagesDir}${c.reset}`);
1078
- console.log("");
1079
- },
1080
- request(method, path10, status, time, extra = {}) {
1081
- const statusColor = getStatusColor(status);
1082
- const timeStr = fmtTime(time);
1083
- let badge = `${c.dim}\u25CB${c.reset}`;
1084
- if (extra.type === "dynamic" || extra.type === "ssr") badge = `${c.white}\u0192${c.reset}`;
1085
- else if (extra.type === "api") badge = `${c.cyan}\u03BB${c.reset}`;
1086
- const statusStr = `${statusColor}${status}${c.reset}`;
1087
- console.log(` ${badge} ${c.white}${method}${c.reset} ${path10} ${statusStr} ${c.dim}${timeStr}${c.reset}`);
1088
- },
1089
- info(msg) {
1090
- console.log(` ${c.cyan}\u2139${c.reset} ${msg}`);
1091
- },
1092
- success(msg) {
1093
- console.log(` ${c.green}\u2714${c.reset} ${msg}`);
1094
- },
1095
- warn(msg) {
1096
- console.log(` ${c.yellow}\u26A0${c.reset} ${c.yellow}${msg}${c.reset}`);
1097
- },
1098
- error(msg, err = null) {
1099
- console.log(` ${c.red}\u2716${c.reset} ${c.red}${msg}${c.reset}`);
1100
- if (err?.stack) {
1101
- console.log("");
1102
- console.log(`${c.dim}${err.stack.split("\n").slice(1, 4).join("\n")}${c.reset}`);
1103
- console.log("");
1104
- }
1105
- },
1106
- compile(file, time) {
1107
- console.log(` ${c.white}\u25CF${c.reset} Compiling ${c.dim}${file}${c.reset} ${c.dim}(${time}ms)${c.reset}`);
1108
- },
1109
- hmr(file) {
1110
- console.log(` ${c.green}\u21BB${c.reset} Fast Refresh ${c.dim}${file}${c.reset}`);
1111
- },
1112
- plugin(name) {
1113
- console.log(` ${c.cyan}\u25C6${c.reset} Plugin ${c.dim}${name}${c.reset}`);
1114
- },
1115
- route(path10, type) {
1116
- const typeLabel = type === "api" ? "\u03BB" : type === "dynamic" ? "\u0192" : "\u25CB";
1117
- const color = type === "api" ? c.cyan : type === "dynamic" ? c.white : c.dim;
1118
- console.log(` ${color}${typeLabel}${c.reset} ${path10}`);
1119
- },
1120
- divider() {
1121
- console.log(`${c.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c.reset}`);
1122
- },
1123
- blank() {
1124
- console.log("");
1125
- },
1126
- portInUse(port) {
1127
- this.error(`Port ${port} is already in use.`);
1128
- this.blank();
1129
- console.log(` ${c.dim}Try:${c.reset}`);
1130
- console.log(` 1. Kill the process on port ${port}`);
1131
- console.log(` 2. Use a different port via PORT env var`);
1132
- this.blank();
1133
- },
1134
- build(stats) {
1135
- this.blank();
1136
- console.log(` ${c.green}\u2714${c.reset} Build completed`);
1137
- this.blank();
1138
- console.log(` ${c.dim}Total time:${c.reset} ${c.white}${stats.time}ms${c.reset}`);
1139
- this.blank();
1140
- }
1141
- };
1142
- var logger_default = logger;
1143
-
1144
- // plugins/tailwind.ts
1145
- function tailwindPlugin(options = {}) {
1146
- const input = options.input || "./app/globals.css";
1147
- const output = options.output || "./public/tailwind.css";
1148
- const configPath = options.config || "./tailwind.config.ts";
1149
- return definePlugin({
1150
- name: "velix:tailwind",
1151
- hooks: {
1152
- [PluginHooks.CONFIG]: (config) => {
1153
- const relativeOutput = output.startsWith("./") ? output.substring(1) : output;
1154
- const stylePath = relativeOutput.startsWith("/public") ? relativeOutput.substring(7) : relativeOutput;
1155
- if (!config.styles) config.styles = [];
1156
- if (!config.styles.includes(stylePath)) {
1157
- config.styles.push(stylePath);
1158
- }
1159
- },
1160
- [PluginHooks.BUILD_START]: async () => {
1161
- logger_default.info("Building Tailwind CSS...");
1162
- try {
1163
- const args = ["tailwindcss", "-i", input, "-o", output];
1164
- if (options.minify !== false) args.push("--minify");
1165
- spawnSync("npx", args, {
1166
- stdio: "inherit",
1167
- cwd: process.cwd()
1168
- });
1169
- logger_default.success("Tailwind CSS built successfully");
1170
- } catch (err) {
1171
- logger_default.error("Tailwind build failed", err);
1172
- }
1173
- },
1174
- [PluginHooks.SERVER_START]: async (server, isDev) => {
1175
- if (!isDev) return;
1176
- logger_default.info("Starting Tailwind CSS watcher...");
1177
- const watcher = spawn("npx", ["tailwindcss", "-i", input, "-o", output, "--watch"], {
1178
- stdio: "pipe",
1179
- cwd: process.cwd()
1180
- });
1181
- watcher.stdout.on("data", (data) => {
1182
- const msg = data.toString().trim();
1183
- if (msg && !msg.includes("Rebuilding...") && !msg.includes("Done in")) {
1184
- logger_default.info(`Tailwind: ${msg}`);
1185
- }
1186
- });
1187
- watcher.stderr.on("data", (data) => {
1188
- const msg = data.toString().trim();
1189
- if (msg) {
1190
- logger_default.warn(`Tailwind: ${msg}`);
1191
- }
1192
- });
1193
- watcher.on("error", (err) => {
1194
- logger_default.error("Tailwind watcher error", err);
1195
- });
1196
- watcher.on("exit", (code) => {
1197
- if (code !== 0 && code !== null) {
1198
- logger_default.error(`Tailwind watcher exited with code ${code}`);
1199
- }
1200
- });
1201
- process.on("exit", () => watcher.kill());
1202
- process.on("SIGINT", () => watcher.kill());
1203
- process.on("SIGTERM", () => watcher.kill());
1204
- }
1205
- }
1206
- });
1207
- }
1208
-
1209
- // metadata/index.ts
1210
- init_esm_shims();
1211
- function generateMetadataTags(metadata, baseUrl) {
1212
- const tags = [];
1213
- const base = baseUrl || metadata.metadataBase?.toString() || "";
1214
- if (metadata.title) {
1215
- const title = typeof metadata.title === "string" ? metadata.title : metadata.title.absolute || (metadata.title.template ? metadata.title.template.replace("%s", metadata.title.default) : metadata.title.default);
1216
- tags.push(`<title>${escapeHtml2(title)}</title>`);
1217
- }
1218
- if (metadata.description) tags.push(`<meta name="description" content="${escapeHtml2(metadata.description)}">`);
1219
- if (metadata.keywords) {
1220
- const kw = Array.isArray(metadata.keywords) ? metadata.keywords.join(", ") : metadata.keywords;
1221
- tags.push(`<meta name="keywords" content="${escapeHtml2(kw)}">`);
1222
- }
1223
- if (metadata.authors) {
1224
- const authors = Array.isArray(metadata.authors) ? metadata.authors : [metadata.authors];
1225
- authors.forEach((a) => {
1226
- if (a.name) tags.push(`<meta name="author" content="${escapeHtml2(a.name)}">`);
1227
- if (a.url) tags.push(`<link rel="author" href="${a.url}">`);
1228
- });
1229
- }
1230
- if (metadata.generator) tags.push(`<meta name="generator" content="${escapeHtml2(metadata.generator)}">`);
1231
- if (metadata.applicationName) tags.push(`<meta name="application-name" content="${escapeHtml2(metadata.applicationName)}">`);
1232
- if (metadata.referrer) tags.push(`<meta name="referrer" content="${metadata.referrer}">`);
1233
- if (metadata.robots) {
1234
- if (typeof metadata.robots === "string") {
1235
- tags.push(`<meta name="robots" content="${metadata.robots}">`);
1236
- } else {
1237
- tags.push(`<meta name="robots" content="${generateRobotsContent(metadata.robots)}">`);
1238
- if (metadata.robots.googleBot) {
1239
- const gbc = typeof metadata.robots.googleBot === "string" ? metadata.robots.googleBot : generateRobotsContent(metadata.robots.googleBot);
1240
- tags.push(`<meta name="googlebot" content="${gbc}">`);
1241
- }
1242
- }
1243
- }
1244
- if (metadata.viewport) {
1245
- const vc = typeof metadata.viewport === "string" ? metadata.viewport : generateViewportContent(metadata.viewport);
1246
- tags.push(`<meta name="viewport" content="${vc}">`);
1247
- }
1248
- if (metadata.themeColor) {
1249
- const tcs = Array.isArray(metadata.themeColor) ? metadata.themeColor : [metadata.themeColor];
1250
- tcs.forEach((tc) => {
1251
- const media = typeof tc !== "string" && tc.media ? ` media="${tc.media}"` : "";
1252
- const color = typeof tc === "string" ? tc : tc.color;
1253
- tags.push(`<meta name="theme-color" content="${color}"${media}>`);
1254
- });
1255
- }
1256
- if (metadata.colorScheme) tags.push(`<meta name="color-scheme" content="${metadata.colorScheme}">`);
1257
- if (metadata.icons) {
1258
- const addIcon = (icon, defaultRel) => {
1259
- const rel = icon.rel || defaultRel;
1260
- const attrs = [
1261
- icon.type ? ` type="${icon.type}"` : "",
1262
- icon.sizes ? ` sizes="${icon.sizes}"` : "",
1263
- icon.color ? ` color="${icon.color}"` : ""
1264
- ].join("");
1265
- tags.push(`<link rel="${rel}" href="${resolveUrl(icon.url, base)}"${attrs}>`);
1266
- };
1267
- if (metadata.icons.icon) {
1268
- (Array.isArray(metadata.icons.icon) ? metadata.icons.icon : [metadata.icons.icon]).forEach((i) => addIcon(i, "icon"));
1269
- }
1270
- if (metadata.icons.apple) {
1271
- (Array.isArray(metadata.icons.apple) ? metadata.icons.apple : [metadata.icons.apple]).forEach((i) => addIcon(i, "apple-touch-icon"));
1272
- }
1273
- }
1274
- if (metadata.manifest) tags.push(`<link rel="manifest" href="${resolveUrl(metadata.manifest, base)}">`);
1275
- if (metadata.openGraph) {
1276
- const og = metadata.openGraph;
1277
- if (og.type) tags.push(`<meta property="og:type" content="${og.type}">`);
1278
- if (og.title) tags.push(`<meta property="og:title" content="${escapeHtml2(og.title)}">`);
1279
- if (og.description) tags.push(`<meta property="og:description" content="${escapeHtml2(og.description)}">`);
1280
- if (og.url) tags.push(`<meta property="og:url" content="${resolveUrl(og.url, base)}">`);
1281
- if (og.siteName) tags.push(`<meta property="og:site_name" content="${escapeHtml2(og.siteName)}">`);
1282
- if (og.locale) tags.push(`<meta property="og:locale" content="${og.locale}">`);
1283
- if (og.images) {
1284
- (Array.isArray(og.images) ? og.images : [og.images]).forEach((img) => {
1285
- tags.push(`<meta property="og:image" content="${resolveUrl(img.url, base)}">`);
1286
- if (img.width) tags.push(`<meta property="og:image:width" content="${img.width}">`);
1287
- if (img.height) tags.push(`<meta property="og:image:height" content="${img.height}">`);
1288
- if (img.alt) tags.push(`<meta property="og:image:alt" content="${escapeHtml2(img.alt)}">`);
1289
- });
1290
- }
1291
- if (og.type === "article") {
1292
- if (og.publishedTime) tags.push(`<meta property="article:published_time" content="${og.publishedTime}">`);
1293
- if (og.modifiedTime) tags.push(`<meta property="article:modified_time" content="${og.modifiedTime}">`);
1294
- if (og.tags) og.tags.forEach((t) => tags.push(`<meta property="article:tag" content="${escapeHtml2(t)}">`));
1295
- }
1296
- }
1297
- if (metadata.twitter) {
1298
- const tw = metadata.twitter;
1299
- if (tw.card) tags.push(`<meta name="twitter:card" content="${tw.card}">`);
1300
- if (tw.site) tags.push(`<meta name="twitter:site" content="${tw.site}">`);
1301
- if (tw.creator) tags.push(`<meta name="twitter:creator" content="${tw.creator}">`);
1302
- if (tw.title) tags.push(`<meta name="twitter:title" content="${escapeHtml2(tw.title)}">`);
1303
- if (tw.description) tags.push(`<meta name="twitter:description" content="${escapeHtml2(tw.description)}">`);
1304
- if (tw.images) {
1305
- (Array.isArray(tw.images) ? tw.images : [tw.images]).forEach((img) => {
1306
- const url = typeof img === "string" ? img : img.url;
1307
- tags.push(`<meta name="twitter:image" content="${resolveUrl(url, base)}">`);
1308
- });
1309
- }
1310
- }
1311
- if (metadata.verification) {
1312
- if (metadata.verification.google) {
1313
- (Array.isArray(metadata.verification.google) ? metadata.verification.google : [metadata.verification.google]).forEach((v) => tags.push(`<meta name="google-site-verification" content="${v}">`));
1314
- }
1315
- }
1316
- if (metadata.alternates) {
1317
- if (metadata.alternates.canonical) tags.push(`<link rel="canonical" href="${resolveUrl(metadata.alternates.canonical, base)}">`);
1318
- if (metadata.alternates.languages) {
1319
- Object.entries(metadata.alternates.languages).forEach(([lang, url]) => {
1320
- tags.push(`<link rel="alternate" hreflang="${lang}" href="${resolveUrl(url, base)}">`);
1321
- });
1322
- }
1323
- }
1324
- return tags.join("\n ");
1325
- }
1326
- function mergeMetadata(parent, child) {
1327
- return {
1328
- ...parent,
1329
- ...child,
1330
- openGraph: child.openGraph ? { ...parent.openGraph, ...child.openGraph } : parent.openGraph,
1331
- twitter: child.twitter ? { ...parent.twitter, ...child.twitter } : parent.twitter,
1332
- icons: child.icons ? { ...parent.icons, ...child.icons } : parent.icons,
1333
- alternates: child.alternates ? { ...parent.alternates, ...child.alternates } : parent.alternates
1334
- };
1335
- }
1336
- function generateJsonLd(data) {
1337
- return `<script type="application/ld+json">${JSON.stringify(data)}</script>`;
1338
- }
1339
- var jsonLd = {
1340
- website: (c2) => ({
1341
- "@context": "https://schema.org",
1342
- "@type": "WebSite",
1343
- name: c2.name,
1344
- url: c2.url,
1345
- description: c2.description
1346
- }),
1347
- article: (c2) => ({
1348
- "@context": "https://schema.org",
1349
- "@type": "Article",
1350
- headline: c2.headline,
1351
- description: c2.description,
1352
- image: c2.image,
1353
- datePublished: c2.datePublished,
1354
- dateModified: c2.dateModified || c2.datePublished,
1355
- author: Array.isArray(c2.author) ? c2.author.map((a) => ({ "@type": "Person", ...a })) : { "@type": "Person", ...c2.author }
1356
- }),
1357
- organization: (c2) => ({
1358
- "@context": "https://schema.org",
1359
- "@type": "Organization",
1360
- name: c2.name,
1361
- url: c2.url,
1362
- logo: c2.logo,
1363
- sameAs: c2.sameAs
1364
- }),
1365
- breadcrumb: (items) => ({
1366
- "@context": "https://schema.org",
1367
- "@type": "BreadcrumbList",
1368
- itemListElement: items.map((item, i) => ({ "@type": "ListItem", position: i + 1, name: item.name, item: item.url }))
1369
- })
1370
- };
1371
- function generateSitemap(routes, baseUrl) {
1372
- const urls = routes.filter((r) => r.type === "page" && !r.path.includes(":") && !r.path.includes("*")).map((r) => {
1373
- const loc = `${baseUrl.replace(/\/$/, "")}${r.path}`;
1374
- return ` <url>
1375
- <loc>${loc}</loc>
1376
- <lastmod>${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}</lastmod>
1377
- <changefreq>weekly</changefreq>
1378
- <priority>${r.path === "/" ? "1.0" : "0.8"}</priority>
1379
- </url>`;
1380
- });
1381
- return `<?xml version="1.0" encoding="UTF-8"?>
1382
- <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
1383
- ${urls.join("\n")}
1384
- </urlset>`;
1385
- }
1386
- function generateRobotsTxt(baseUrl, options = {}) {
1387
- const lines = ["User-agent: *"];
1388
- if (options.allow) options.allow.forEach((p) => lines.push(`Allow: ${p}`));
1389
- if (options.disallow) options.disallow.forEach((p) => lines.push(`Disallow: ${p}`));
1390
- else lines.push("Allow: /");
1391
- lines.push("", `Sitemap: ${baseUrl.replace(/\/$/, "")}/sitemap.xml`);
1392
- return lines.join("\n");
1393
- }
1394
- function escapeHtml2(str) {
1395
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1396
- }
1397
- function resolveUrl(url, base) {
1398
- if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("//")) return url;
1399
- return base ? `${base.replace(/\/$/, "")}${url.startsWith("/") ? "" : "/"}${url}` : url;
1400
- }
1401
- function generateRobotsContent(robots) {
1402
- const parts = [];
1403
- if (robots.index !== void 0) parts.push(robots.index ? "index" : "noindex");
1404
- if (robots.follow !== void 0) parts.push(robots.follow ? "follow" : "nofollow");
1405
- if (robots.noarchive) parts.push("noarchive");
1406
- if (robots.nosnippet) parts.push("nosnippet");
1407
- if (robots.noimageindex) parts.push("noimageindex");
1408
- return parts.join(", ") || "index, follow";
1409
- }
1410
- function generateViewportContent(viewport) {
1411
- const parts = [];
1412
- if (viewport.width) parts.push(`width=${viewport.width}`);
1413
- if (viewport.height) parts.push(`height=${viewport.height}`);
1414
- if (viewport.initialScale !== void 0) parts.push(`initial-scale=${viewport.initialScale}`);
1415
- if (viewport.maximumScale !== void 0) parts.push(`maximum-scale=${viewport.maximumScale}`);
1416
- if (viewport.userScalable !== void 0) parts.push(`user-scalable=${viewport.userScalable ? "yes" : "no"}`);
1417
- return parts.join(", ") || "width=device-width, initial-scale=1";
1418
- }
1419
-
1420
- // islands/index.ts
1421
- init_esm_shims();
1422
- import React from "react";
1423
- import crypto2 from "crypto";
1424
- var islandRegistry = /* @__PURE__ */ new Map();
1425
- function generateIslandId(componentName) {
1426
- const hash = crypto2.randomBytes(4).toString("hex");
1427
- return `island-${componentName}-${hash}`;
1428
- }
1429
- function Island({ component: Component, props = {}, name, clientPath }) {
1430
- const islandId = generateIslandId(name);
1431
- islandRegistry.set(islandId, { id: islandId, name, clientPath, props });
1432
- return React.createElement("div", {
1433
- "data-island": islandId,
1434
- "data-island-name": name,
1435
- "data-island-props": JSON.stringify(props)
1436
- }, React.createElement(Component, props));
1437
- }
1438
- function getRegisteredIslands() {
1439
- const islands = Array.from(islandRegistry.values());
1440
- islandRegistry.clear();
1441
- return islands;
1442
- }
1443
- function createIsland(Component, options = {}) {
1444
- const { name = Component.name || "Island", clientPath } = options;
1445
- function IslandWrapper(props) {
1446
- return Island({
1447
- component: Component,
1448
- props,
1449
- name,
1450
- clientPath: clientPath || `/__velix/islands/${name}.js`
1451
- });
1452
- }
1453
- IslandWrapper.displayName = `Island(${name})`;
1454
- IslandWrapper.isIsland = true;
1455
- IslandWrapper.originalComponent = Component;
1456
- return IslandWrapper;
1457
- }
1458
- var LoadStrategy = {
1459
- IMMEDIATE: "immediate",
1460
- VISIBLE: "visible",
1461
- IDLE: "idle",
1462
- MEDIA: "media"
1463
- };
1464
- function createLazyIsland(Component, options = {}) {
1465
- const {
1466
- name = Component.name || "LazyIsland",
1467
- clientPath,
1468
- strategy = LoadStrategy.VISIBLE,
1469
- media = null
1470
- } = options;
1471
- function LazyIslandWrapper(props) {
1472
- const islandId = generateIslandId(name);
1473
- islandRegistry.set(islandId, {
1474
- id: islandId,
1475
- name,
1476
- clientPath: clientPath || `/__velix/islands/${name}.js`,
1477
- props,
1478
- strategy,
1479
- media
1480
- });
1481
- return React.createElement("div", {
1482
- "data-island": islandId,
1483
- "data-island-name": name,
1484
- "data-island-strategy": strategy,
1485
- "data-island-media": media,
1486
- "data-island-props": JSON.stringify(props)
1487
- });
1488
- }
1489
- LazyIslandWrapper.displayName = `LazyIsland(${name})`;
1490
- LazyIslandWrapper.isIsland = true;
1491
- LazyIslandWrapper.isLazy = true;
1492
- return LazyIslandWrapper;
1493
- }
1494
- function generateHydrationScript(islands) {
1495
- if (!islands.length) return "";
1496
- const islandData = islands.map((island) => ({
1497
- id: island.id,
1498
- name: island.name,
1499
- path: island.clientPath,
1500
- props: island.props
1501
- }));
1502
- return `
1503
- <script type="module">
1504
- const islands = ${JSON.stringify(islandData)};
1505
-
1506
- async function hydrateIslands() {
1507
- const { hydrateRoot } = await import('/__velix/react-dom-client.js');
1508
- const React = await import('/__velix/react.js');
1509
-
1510
- for (const island of islands) {
1511
- try {
1512
- const el = document.querySelector(\`[data-island="\${island.id}"]\`);
1513
- if (!el) continue;
1514
- const mod = await import(island.path);
1515
- hydrateRoot(el, React.createElement(mod.default, island.props));
1516
- el.setAttribute('data-hydrated', 'true');
1517
- } catch (error) {
1518
- console.error(\`Failed to hydrate island \${island.name}:\`, error);
1519
- }
1520
- }
1521
- }
1522
-
1523
- if (document.readyState === 'loading') {
1524
- document.addEventListener('DOMContentLoaded', hydrateIslands);
1525
- } else {
1526
- hydrateIslands();
1527
- }
1528
- </script>`;
1529
- }
1530
- function generateAdvancedHydrationScript(islands) {
1531
- if (!islands.length) return "";
1532
- const islandData = islands.map((island) => ({
1533
- id: island.id,
1534
- name: island.name,
1535
- path: island.clientPath,
1536
- props: island.props,
1537
- strategy: island.strategy || LoadStrategy.IMMEDIATE,
1538
- media: island.media
1539
- }));
1540
- return `
1541
- <script type="module">
1542
- const islands = ${JSON.stringify(islandData)};
1543
-
1544
- async function hydrateIsland(island) {
1545
- const el = document.querySelector(\`[data-island="\${island.id}"]\`);
1546
- if (!el || el.hasAttribute('data-hydrated')) return;
1547
-
1548
- try {
1549
- const { hydrateRoot } = await import('/__velix/react-dom-client.js');
1550
- const React = await import('/__velix/react.js');
1551
- const mod = await import(island.path);
1552
- hydrateRoot(el, React.createElement(mod.default, island.props));
1553
- el.setAttribute('data-hydrated', 'true');
1554
- } catch (error) {
1555
- console.error(\`Failed to hydrate island \${island.name}:\`, error);
1556
- }
1557
- }
1558
-
1559
- const visibleObserver = new IntersectionObserver((entries) => {
1560
- entries.forEach(entry => {
1561
- if (entry.isIntersecting) {
1562
- const id = entry.target.getAttribute('data-island');
1563
- const island = islands.find(i => i.id === id);
1564
- if (island) { hydrateIsland(island); visibleObserver.unobserve(entry.target); }
1565
- }
1566
- });
1567
- }, { rootMargin: '50px' });
1568
-
1569
- function processIslands() {
1570
- for (const island of islands) {
1571
- const el = document.querySelector(\`[data-island="\${island.id}"]\`);
1572
- if (!el) continue;
1573
- switch (island.strategy) {
1574
- case 'immediate': hydrateIsland(island); break;
1575
- case 'visible': visibleObserver.observe(el); break;
1576
- case 'idle': requestIdleCallback(() => hydrateIsland(island)); break;
1577
- case 'media':
1578
- if (island.media && window.matchMedia(island.media).matches) hydrateIsland(island);
1579
- break;
1580
- }
1581
- }
1582
- }
1583
-
1584
- if (document.readyState === 'loading') {
1585
- document.addEventListener('DOMContentLoaded', processIslands);
1586
- } else {
1587
- processIslands();
1588
- }
1589
- </script>`;
1590
- }
1591
-
1592
- // actions/index.ts
1593
- init_esm_shims();
1594
- import { useActionState, useOptimistic } from "react";
1595
- import { useFormStatus } from "react-dom";
1596
- import { useActionState as useActionStateReact } from "react";
1597
- globalThis.__VELIX_ACTIONS__ = globalThis.__VELIX_ACTIONS__ || {};
1598
- globalThis.__VELIX_ACTION_CONTEXT__ = null;
1599
- function serverAction(fn, actionId) {
1600
- const id = actionId || `action_${fn.name}_${generateActionId()}`;
1601
- globalThis.__VELIX_ACTIONS__[id] = fn;
1602
- const proxy = (async (...args) => {
1603
- if (typeof window === "undefined") return await executeAction(id, args);
1604
- return await callServerAction(id, args);
1605
- });
1606
- proxy.$$typeof = /* @__PURE__ */ Symbol.for("react.server.action");
1607
- proxy.$$id = id;
1608
- proxy.$$bound = null;
1609
- return proxy;
1610
- }
1611
- function registerAction(id, fn) {
1612
- globalThis.__VELIX_ACTIONS__[id] = fn;
1613
- }
1614
- function getAction(id) {
1615
- return globalThis.__VELIX_ACTIONS__[id];
1616
- }
1617
- async function executeAction(actionId, args, context) {
1618
- const action = globalThis.__VELIX_ACTIONS__[actionId];
1619
- if (!action) return { success: false, error: `Server action not found: ${actionId}` };
1620
- const actionContext = {
1621
- request: context?.request || new Request("http://localhost"),
1622
- cookies,
1623
- headers,
1624
- redirect,
1625
- notFound
1626
- };
1627
- globalThis.__VELIX_ACTION_CONTEXT__ = actionContext;
1628
- try {
1629
- const result = await action(...args);
1630
- return { success: true, data: result };
1631
- } catch (error) {
1632
- if (error instanceof RedirectError) return { success: true, redirect: error.url };
1633
- if (error instanceof NotFoundError) return { success: false, error: "Not found" };
1634
- return { success: false, error: error.message || "Action failed" };
1635
- } finally {
1636
- globalThis.__VELIX_ACTION_CONTEXT__ = null;
1637
- }
1638
- }
1639
- async function callServerAction(actionId, args) {
1640
- try {
1641
- const response = await fetch("/__velix/action", {
1642
- method: "POST",
1643
- headers: { "Content-Type": "application/json", "X-Velix-Action": actionId },
1644
- body: JSON.stringify({ actionId, args: serializeArgs(args) }),
1645
- credentials: "same-origin"
1646
- });
1647
- if (!response.ok) throw new Error(`Action failed: ${response.statusText}`);
1648
- const result = await response.json();
1649
- if (result.redirect) {
1650
- window.location.href = result.redirect;
1651
- }
1652
- return result;
1653
- } catch (error) {
1654
- return { success: false, error: error.message || "Network error" };
1655
- }
1656
- }
1657
- function serializeArgs(args) {
1658
- return args.map((arg) => {
1659
- if (arg instanceof FormData) {
1660
- const obj = {};
1661
- arg.forEach((value, key) => {
1662
- if (obj[key]) {
1663
- obj[key] = Array.isArray(obj[key]) ? [...obj[key], value] : [obj[key], value];
1664
- } else {
1665
- obj[key] = value;
1666
- }
1667
- });
1668
- return { $$type: "FormData", data: obj };
1669
- }
1670
- if (arg instanceof Date) return { $$type: "Date", value: arg.toISOString() };
1671
- if (typeof arg === "object" && arg !== null) return JSON.parse(JSON.stringify(arg));
1672
- return arg;
1673
- });
1674
- }
1675
- var ALLOWED_TYPES = /* @__PURE__ */ new Set(["FormData", "Date", "File"]);
1676
- var MAX_DEPTH = 10;
1677
- function validateInput(obj, depth = 0) {
1678
- if (depth > MAX_DEPTH) throw new Error("Payload too deeply nested");
1679
- if (obj === null || obj === void 0 || typeof obj !== "object") return true;
1680
- if ("__proto__" in obj || "constructor" in obj || "prototype" in obj) {
1681
- throw new Error("Invalid payload: prototype pollution attempt detected");
1682
- }
1683
- if ("$$type" in obj && !ALLOWED_TYPES.has(obj.$$type)) {
1684
- throw new Error(`Invalid serialized type: ${obj.$$type}`);
1685
- }
1686
- if (Array.isArray(obj)) obj.forEach((item) => validateInput(item, depth + 1));
1687
- else Object.values(obj).forEach((val) => validateInput(val, depth + 1));
1688
- return true;
1689
- }
1690
- function deserializeArgs(args) {
1691
- validateInput(args);
1692
- return args.map((arg) => {
1693
- if (arg && typeof arg === "object") {
1694
- if (arg.$$type === "FormData") {
1695
- const fd = new FormData();
1696
- for (const [key, value] of Object.entries(arg.data || {})) {
1697
- if (typeof key !== "string" || key.startsWith("__")) continue;
1698
- if (Array.isArray(value)) value.forEach((v) => {
1699
- if (typeof v === "string" || typeof v === "number") fd.append(key, String(v));
1700
- });
1701
- else if (typeof value === "string" || typeof value === "number") fd.append(key, String(value));
1702
- }
1703
- return fd;
1704
- }
1705
- if (arg.$$type === "Date") {
1706
- const d = new Date(arg.value);
1707
- if (isNaN(d.getTime())) throw new Error("Invalid date");
1708
- return d;
1709
- }
1710
- if (arg.$$type === "File") return { name: String(arg.name || ""), type: String(arg.type || ""), size: Number(arg.size || 0) };
1711
- }
1712
- return arg;
1713
- });
1714
- }
1715
- function generateActionId() {
1716
- return Math.random().toString(36).substring(2, 10);
1717
- }
1718
- function useActionContext() {
1719
- return globalThis.__VELIX_ACTION_CONTEXT__;
1720
- }
1721
- function formAction(action) {
1722
- return async (formData) => {
1723
- try {
1724
- const result = await action(formData);
1725
- return { success: true, data: result };
1726
- } catch (error) {
1727
- if (error instanceof RedirectError) return { success: true, redirect: error.url };
1728
- return { success: false, error: error.message };
1729
- }
1730
- };
1731
- }
1732
- function useVelixAction(action, initialState, permalink) {
1733
- return useActionStateReact(action, initialState, permalink);
1734
- }
1735
- function bindArgs(action, ...boundArgs) {
1736
- const bound = (async (...args) => await action(...boundArgs, ...args));
1737
- bound.$$typeof = action.$$typeof;
1738
- bound.$$id = action.$$id;
1739
- bound.$$bound = boundArgs;
1740
- return bound;
1741
- }
1742
-
1743
- // server/index.ts
1744
- import esbuild from "esbuild";
1745
-
1746
- // server/devtools.ts
1747
- init_esm_shims();
1748
- function generateDevToolsHtml(isDev) {
1749
- if (!isDev) return "";
1750
- return `<style>
1751
- @keyframes velix-pulse {
1752
- 0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(34, 211, 238, 0.7); }
1753
- 70% { transform: scale(1.05); box-shadow: 0 0 0 10px rgba(34, 211, 238, 0); }
1754
- 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(34, 211, 238, 0); }
1755
- }
1756
-
1757
- @keyframes velix-spin {
1758
- from { transform: rotate(0deg); }
1759
- to { transform: rotate(360deg); }
1760
- }
1761
-
1762
- .velix-idle { background: #0f172a !important; border: 2px solid #22D3EE !important; }
1763
- .velix-rendering { background: #ea580c !important; border: 2px solid #fb923c !important; animation: velix-pulse 1.5s infinite !important; }
1764
- .velix-compiling { background: #16a34a !important; border: 2px solid #4ade80 !important; animation: velix-spin 2s linear infinite !important; }
1765
- .velix-navigating { background: #2563eb !important; border: 2px solid #60a5fa !important; animation: velix-pulse 1s infinite !important; }
1766
- .velix-error { background: #dc2626 !important; border: 2px solid #f87171 !important; animation: velix-pulse 0.8s infinite !important; }
1767
-
1768
- .velix-status-badge {
1769
- position: absolute;
1770
- top: -4px;
1771
- right: -4px;
1772
- width: 12px;
1773
- height: 12px;
1774
- border-radius: 50%;
1775
- border: 2px solid #0f172a;
1776
- }
1777
-
1778
- .velix-status-idle { background: #22D3EE; }
1779
- .velix-status-rendering { background: #fb923c; }
1780
- .velix-status-compiling { background: #4ade80; }
1781
- .velix-status-navigating { background: #60a5fa; }
1782
- .velix-status-error { background: #f87171; }
1783
- </style>
1784
-
1785
- <script>
1786
- // DevTools State Management
1787
- window.__VELIX_DEV_TOOLS__ = {
1788
- status: 'idle',
1789
- setStatus: function(newStatus) {
1790
- this.status = newStatus;
1791
- const widget = document.getElementById('__velix-dev-tools');
1792
- const badge = document.getElementById('__velix-status-badge');
1793
- const statusText = document.getElementById('__velix-status-text');
1794
-
1795
- if (widget) {
1796
- widget.className = 'velix-' + newStatus;
1797
- }
1798
-
1799
- if (badge) {
1800
- badge.className = 'velix-status-badge velix-status-' + newStatus;
1801
- }
1802
-
1803
- if (statusText) {
1804
- const statusLabels = {
1805
- idle: 'Ready',
1806
- rendering: 'Rendering',
1807
- compiling: 'Compiling',
1808
- navigating: 'Navigating',
1809
- error: 'Error'
1810
- };
1811
- statusText.textContent = statusLabels[newStatus] || 'Unknown';
1812
- statusText.style.color = {
1813
- idle: '#22D3EE',
1814
- rendering: '#fb923c',
1815
- compiling: '#4ade80',
1816
- navigating: '#60a5fa',
1817
- error: '#f87171'
1818
- }[newStatus] || '#94a3b8';
1819
- }
1820
- }
1821
- };
1822
-
1823
- // HMR Connection
1824
- const es = new EventSource('/__velix/hmr');
1825
- es.onmessage = (e) => {
1826
- const data = e.data;
1827
-
1828
- if (data === 'reload') {
1829
- window.__VELIX_DEV_TOOLS__.setStatus('idle');
1830
- setTimeout(() => location.reload(), 100);
1831
- }
1832
-
1833
- if (data === 'building') {
1834
- window.__VELIX_DEV_TOOLS__.setStatus('compiling');
1835
- }
1836
-
1837
- if (data === 'built') {
1838
- window.__VELIX_DEV_TOOLS__.setStatus('idle');
1839
- }
1840
-
1841
- if (data.startsWith('rendering:')) {
1842
- window.__VELIX_DEV_TOOLS__.setStatus('rendering');
1843
- setTimeout(() => window.__VELIX_DEV_TOOLS__.setStatus('idle'), 1000);
1844
- }
1845
-
1846
- if (data.startsWith('error:')) {
1847
- window.__VELIX_DEV_TOOLS__.setStatus('error');
1848
- }
1849
- };
1850
-
1851
- es.onerror = () => {
1852
- window.__VELIX_DEV_TOOLS__.setStatus('error');
1853
- };
1854
- </script>
1855
-
1856
- <div id="__velix-dev-tools" class="velix-idle" style="position:fixed;bottom:16px;left:16px;z-index:9999;border-radius:50%;padding:4px;box-shadow:0 4px 12px rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;width:40px;height:40px;cursor:pointer;transition:all 0.3s ease;" onmouseover="this.style.transform='scale(1.1)'" onmouseout="this.style.transform='scale(1)'" onclick="document.getElementById('__velix-dev-panel').style.display='block'" title="Velix DevTools">
1857
- <img src="/__velix/logo.webp" alt="Velix DevTools" style="width:22px;height:22px;" />
1858
- <div id="__velix-status-badge" class="velix-status-badge velix-status-idle"></div>
1859
- </div>
1860
-
1861
- <div id="__velix-dev-panel" style="display:none;position:fixed;bottom:70px;left:16px;width:320px;background:#0f172a;color:white;border-radius:16px;padding:20px;font-family:ui-sans-serif,system-ui,-apple-system,sans-serif;box-shadow:0 20px 50px rgba(0,0,0,0.4);z-index:10000;border:1px solid #1e293b;">
1862
- <div style="display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #334155;padding-bottom:16px;margin-bottom:16px;">
1863
- <h3 style="margin:0;font-size:16px;font-weight:700;display:flex;align-items:center;gap:10px;">
1864
- <img src="/__velix/logo.webp" style="width:18px;height:18px;" />
1865
- Velix DevTools
1866
- </h3>
1867
- <button onclick="document.getElementById('__velix-dev-panel').style.display='none'" style="background:transparent;border:none;color:#94a3b8;cursor:pointer;font-size:22px;line-height:1;padding:0;margin:0;transition:color 0.2s;" onmouseover="this.style.color='white'" onmouseout="this.style.color='#94a3b8'">&times;</button>
1868
- </div>
1869
-
1870
- <div style="margin-bottom:16px;padding:12px;background:#1e293b;border-radius:8px;border:1px solid #334155;">
1871
- <div style="font-size:12px;color:#94a3b8;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.5px;">Status</div>
1872
- <div id="__velix-status-text" style="font-size:16px;font-weight:600;color:#22D3EE;">Ready</div>
1873
- </div>
1874
-
1875
- <div style="font-size:13px;color:#cbd5e1;line-height:2;">
1876
- <div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #1e293b;">
1877
- <span style="color:#94a3b8;">Framework</span>
1878
- <strong style="color:white;font-weight:600;">v5.0.0</strong>
1879
- </div>
1880
- <div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #1e293b;">
1881
- <span style="color:#94a3b8;">Environment</span>
1882
- <strong style="color:#10b981;font-weight:600;">Development</strong>
1883
- </div>
1884
- <div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid #1e293b;">
1885
- <span style="color:#94a3b8;">Router</span>
1886
- <strong style="color:white;font-weight:600;">App Directory</strong>
1887
- </div>
1888
- <div style="display:flex;justify-content:space-between;padding:8px 0;">
1889
- <span style="color:#94a3b8;">Rendering</span>
1890
- <strong style="color:#60a5fa;font-weight:600;">Streaming SSR</strong>
1891
- </div>
1892
- </div>
1893
-
1894
- <div style="margin-top:16px;padding-top:16px;border-top:1px solid #334155;">
1895
- <div style="font-size:11px;color:#64748b;text-align:center;">
1896
- <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#22D3EE;margin-right:4px;"></span> Idle
1897
- <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#fb923c;margin:0 4px 0 12px;"></span> Rendering
1898
- <span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:#4ade80;margin:0 4px 0 12px;"></span> Compiling
1899
- </div>
1900
- </div>
1901
- </div>`;
1902
- }
1903
-
1904
- // server/error-pages.ts
1905
- init_esm_shims();
1906
- function generate404Page(pathname = "/") {
1907
- return `<!DOCTYPE html>
1908
- <html lang="en">
1909
- <head>
1910
- <meta charset="UTF-8">
1911
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1912
- <title>404 - Page Not Found | Velix v5</title>
1913
- <style>
1914
- * { margin: 0; padding: 0; box-sizing: border-box; }
1915
- body {
1916
- margin: 0;
1917
- background: linear-gradient(135deg, #0F172A 0%, #1E293B 100%);
1918
- color: white;
1919
- font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1920
- display: flex;
1921
- align-items: center;
1922
- justify-content: center;
1923
- min-height: 100vh;
1924
- text-align: center;
1925
- overflow: hidden;
1926
- }
1927
- .container {
1928
- max-width: 700px;
1929
- padding: 60px 40px;
1930
- position: relative;
1931
- z-index: 10;
1932
- }
1933
- .bg-pattern {
1934
- position: absolute;
1935
- inset: 0;
1936
- background-image: radial-gradient(circle at 20% 50%, rgba(34, 211, 238, 0.1) 0%, transparent 50%),
1937
- radial-gradient(circle at 80% 80%, rgba(37, 99, 235, 0.1) 0%, transparent 50%);
1938
- z-index: 1;
1939
- }
1940
- h1 {
1941
- font-size: 180px;
1942
- font-weight: 900;
1943
- margin: 0;
1944
- background: linear-gradient(135deg, #22D3EE 0%, #2563EB 100%);
1945
- -webkit-background-clip: text;
1946
- -webkit-text-fill-color: transparent;
1947
- line-height: 1;
1948
- letter-spacing: -0.05em;
1949
- animation: fadeInUp 0.6s ease-out;
1950
- }
1951
- h2 {
1952
- font-size: 36px;
1953
- font-weight: 800;
1954
- margin: 30px 0 15px;
1955
- color: #F8FAFC;
1956
- animation: fadeInUp 0.6s ease-out 0.1s backwards;
1957
- }
1958
- p {
1959
- color: #94A3B8;
1960
- font-size: 18px;
1961
- line-height: 1.7;
1962
- margin-bottom: 40px;
1963
- animation: fadeInUp 0.6s ease-out 0.2s backwards;
1964
- }
1965
- code {
1966
- background: rgba(255,255,255,0.1);
1967
- padding: 4px 10px;
1968
- border-radius: 6px;
1969
- font-family: 'Fira Code', 'Courier New', monospace;
1970
- color: #22D3EE;
1971
- font-size: 16px;
1972
- border: 1px solid rgba(34, 211, 238, 0.2);
1973
- }
1974
- .btn-group {
1975
- display: flex;
1976
- gap: 16px;
1977
- justify-content: center;
1978
- flex-wrap: wrap;
1979
- animation: fadeInUp 0.6s ease-out 0.3s backwards;
1980
- }
1981
- .btn {
1982
- display: inline-block;
1983
- background: linear-gradient(135deg, #2563EB 0%, #1D4ED8 100%);
1984
- color: white;
1985
- text-decoration: none;
1986
- padding: 14px 36px;
1987
- border-radius: 12px;
1988
- font-weight: 600;
1989
- font-size: 16px;
1990
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1991
- box-shadow: 0 10px 30px rgba(37, 99, 235, 0.3);
1992
- border: none;
1993
- cursor: pointer;
1994
- }
1995
- .btn:hover {
1996
- transform: translateY(-2px);
1997
- box-shadow: 0 15px 40px rgba(37, 99, 235, 0.4);
1998
- }
1999
- .btn-outline {
2000
- background: transparent;
2001
- color: #22D3EE;
2002
- border: 2px solid #22D3EE;
2003
- box-shadow: none;
2004
- }
2005
- .btn-outline:hover {
2006
- background: rgba(34, 211, 238, 0.1);
2007
- box-shadow: 0 10px 30px rgba(34, 211, 238, 0.2);
2008
- }
2009
- @keyframes fadeInUp {
2010
- from {
2011
- opacity: 0;
2012
- transform: translateY(30px);
2013
- }
2014
- to {
2015
- opacity: 1;
2016
- transform: translateY(0);
2017
- }
2018
- }
2019
- </style>
2020
- </head>
2021
- <body>
2022
- <div class="bg-pattern"></div>
2023
- <div class="container">
2024
- <h1>404</h1>
2025
- <h2>Page Not Found</h2>
2026
- <p>The page <code>${pathname}</code> could not be found.<br>It may have been moved or deleted.</p>
2027
- <div class="btn-group">
2028
- <a href="/" class="btn">Return Home</a>
2029
- <a href="javascript:history.back()" class="btn btn-outline">Go Back</a>
2030
- </div>
2031
- </div>
2032
- </body>
2033
- </html>`;
2034
- }
2035
- function generate500Page(options) {
2036
- const { title, message, stack, isDev, pathname } = options;
2037
- let callStackCards = "";
2038
- let frameCount = 0;
2039
- if (isDev && stack) {
2040
- const stackLines = stack.split("\n").filter((line) => line.trim());
2041
- const frames = stackLines.slice(1).filter((line) => line.includes("at "));
2042
- frameCount = frames.length;
2043
- callStackCards = frames.map((frame, index) => {
2044
- const match = frame.match(/at\s+(.+?)\s+\((.+?)\)/) || frame.match(/at\s+(.+)/);
2045
- if (match) {
2046
- const funcName = match[1] || "anonymous";
2047
- const location = match[2] || match[1];
2048
- return `
2049
- <div class="error-card" data-frame="${index}">
2050
- <div class="card-header">
2051
- <div class="card-title">${funcName.trim()}</div>
2052
- <div class="card-number">${index + 1}</div>
2053
- </div>
2054
- <div class="card-location">${location.trim()}</div>
2055
- </div>
2056
- `;
2057
- }
2058
- return "";
2059
- }).join("");
2060
- }
2061
- return `<!DOCTYPE html>
2062
- <html lang="en">
2063
- <head>
2064
- <meta charset="UTF-8">
2065
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
2066
- <title>Error | Velix v5</title>
2067
- <style>
2068
- * { margin: 0; padding: 0; box-sizing: border-box; }
2069
- body {
2070
- margin: 0;
2071
- background: linear-gradient(135deg, #0F172A 0%, #1E293B 100%);
2072
- color: #F8FAFC;
2073
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2074
- min-height: 100vh;
2075
- overflow-x: hidden;
2076
- }
2077
- .container {
2078
- max-width: 900px;
2079
- margin: 0 auto;
2080
- padding: 40px 20px;
2081
- }
2082
- .error-header {
2083
- background: linear-gradient(135deg, #EF4444 0%, #DC2626 100%);
2084
- color: white;
2085
- padding: 20px 28px;
2086
- border-radius: 16px;
2087
- display: flex;
2088
- align-items: center;
2089
- gap: 14px;
2090
- margin-bottom: 28px;
2091
- font-size: 18px;
2092
- font-weight: 700;
2093
- box-shadow: 0 10px 40px rgba(239, 68, 68, 0.3);
2094
- }
2095
- .error-header svg {
2096
- width: 24px;
2097
- height: 24px;
2098
- flex-shrink: 0;
2099
- }
2100
- .error-badge {
2101
- display: inline-block;
2102
- background: linear-gradient(135deg, #1E40AF 0%, #1E3A8A 100%);
2103
- color: #60A5FA;
2104
- padding: 6px 16px;
2105
- border-radius: 8px;
2106
- font-size: 13px;
2107
- font-weight: 700;
2108
- margin-bottom: 18px;
2109
- text-transform: uppercase;
2110
- letter-spacing: 0.8px;
2111
- box-shadow: 0 4px 12px rgba(30, 64, 175, 0.3);
2112
- }
2113
- .route-badge {
2114
- background: linear-gradient(135deg, #0C4A6E 0%, #075985 100%);
2115
- color: #22D3EE;
2116
- padding: 10px 18px;
2117
- border-radius: 10px;
2118
- font-size: 14px;
2119
- margin-bottom: 24px;
2120
- font-family: 'Courier New', monospace;
2121
- box-shadow: 0 4px 12px rgba(12, 74, 110, 0.3);
2122
- border: 1px solid rgba(34, 211, 238, 0.2);
2123
- }
2124
- .error-message {
2125
- background: rgba(239, 68, 68, 0.1);
2126
- border-left: 4px solid #EF4444;
2127
- padding: 18px 20px;
2128
- border-radius: 10px;
2129
- margin-bottom: 32px;
2130
- }
2131
- .error-message-text {
2132
- color: #FCA5A5;
2133
- font-size: 16px;
2134
- font-weight: 600;
2135
- line-height: 1.6;
2136
- font-family: 'Courier New', monospace;
2137
- }
2138
- .stack-section {
2139
- margin-top: 32px;
2140
- }
2141
- .stack-header {
2142
- display: flex;
2143
- align-items: center;
2144
- justify-content: space-between;
2145
- margin-bottom: 20px;
2146
- }
2147
- .stack-title {
2148
- font-size: 18px;
2149
- font-weight: 700;
2150
- color: #F1F5F9;
2151
- display: flex;
2152
- align-items: center;
2153
- gap: 10px;
2154
- }
2155
- .frame-counter {
2156
- background: linear-gradient(135deg, #1F2937 0%, #111827 100%);
2157
- color: #22D3EE;
2158
- padding: 6px 14px;
2159
- border-radius: 8px;
2160
- font-size: 13px;
2161
- font-weight: 700;
2162
- border: 1px solid rgba(34, 211, 238, 0.2);
2163
- }
2164
- .error-card {
2165
- background: linear-gradient(135deg, #1E293B 0%, #0F172A 100%);
2166
- border: 1px solid rgba(34, 211, 238, 0.2);
2167
- border-radius: 14px;
2168
- padding: 20px;
2169
- margin-bottom: 12px;
2170
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
2171
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
2172
- display: none;
2173
- }
2174
- .error-card.active {
2175
- display: block;
2176
- animation: slideIn 0.3s ease-out;
2177
- }
2178
- .error-card:hover {
2179
- border-color: rgba(34, 211, 238, 0.5);
2180
- box-shadow: 0 8px 24px rgba(34, 211, 238, 0.2);
2181
- transform: translateY(-2px);
2182
- }
2183
- .card-header {
2184
- display: flex;
2185
- justify-content: space-between;
2186
- align-items: center;
2187
- margin-bottom: 12px;
2188
- }
2189
- .card-title {
2190
- color: #22D3EE;
2191
- font-size: 15px;
2192
- font-weight: 700;
2193
- font-family: 'Courier New', monospace;
2194
- }
2195
- .card-number {
2196
- background: rgba(34, 211, 238, 0.2);
2197
- color: #22D3EE;
2198
- padding: 4px 10px;
2199
- border-radius: 6px;
2200
- font-size: 12px;
2201
- font-weight: 700;
2202
- }
2203
- .card-location {
2204
- color: #94A3B8;
2205
- font-size: 13px;
2206
- font-family: 'Courier New', monospace;
2207
- word-break: break-all;
2208
- line-height: 1.6;
2209
- }
2210
- .pagination {
2211
- display: flex;
2212
- justify-content: center;
2213
- align-items: center;
2214
- gap: 12px;
2215
- margin-top: 24px;
2216
- }
2217
- .pagination-btn {
2218
- background: linear-gradient(135deg, #1E293B 0%, #0F172A 100%);
2219
- border: 1px solid rgba(34, 211, 238, 0.3);
2220
- color: #22D3EE;
2221
- padding: 10px 20px;
2222
- border-radius: 10px;
2223
- font-size: 14px;
2224
- font-weight: 600;
2225
- cursor: pointer;
2226
- transition: all 0.2s;
2227
- }
2228
- .pagination-btn:hover:not(:disabled) {
2229
- background: linear-gradient(135deg, #22D3EE 0%, #06B6D4 100%);
2230
- color: #0F172A;
2231
- transform: translateY(-2px);
2232
- box-shadow: 0 6px 20px rgba(34, 211, 238, 0.4);
2233
- }
2234
- .pagination-btn:disabled {
2235
- opacity: 0.3;
2236
- cursor: not-allowed;
2237
- }
2238
- .pagination-info {
2239
- color: #94A3B8;
2240
- font-size: 14px;
2241
- font-weight: 600;
2242
- }
2243
- .footer {
2244
- margin-top: 48px;
2245
- padding-top: 28px;
2246
- border-top: 1px solid rgba(34, 211, 238, 0.2);
2247
- display: flex;
2248
- justify-content: space-between;
2249
- align-items: center;
2250
- flex-wrap: wrap;
2251
- gap: 20px;
2252
- }
2253
- .brand {
2254
- display: flex;
2255
- align-items: center;
2256
- gap: 10px;
2257
- font-weight: 700;
2258
- color: #22D3EE;
2259
- font-size: 15px;
2260
- }
2261
- .brand img {
2262
- width: 20px;
2263
- height: 20px;
2264
- }
2265
- .footer-links {
2266
- display: flex;
2267
- gap: 24px;
2268
- flex-wrap: wrap;
2269
- }
2270
- .footer-links a {
2271
- color: #60A5FA;
2272
- text-decoration: none;
2273
- font-weight: 600;
2274
- transition: all 0.2s;
2275
- font-size: 14px;
2276
- }
2277
- .footer-links a:hover {
2278
- color: #22D3EE;
2279
- transform: translateY(-1px);
2280
- }
2281
- .no-stack {
2282
- background: rgba(239, 68, 68, 0.1);
2283
- border: 1px solid rgba(239, 68, 68, 0.3);
2284
- border-radius: 12px;
2285
- padding: 32px;
2286
- color: #FCA5A5;
2287
- text-align: center;
2288
- font-size: 15px;
2289
- }
2290
- @keyframes slideIn {
2291
- from {
2292
- opacity: 0;
2293
- transform: translateY(10px);
2294
- }
2295
- to {
2296
- opacity: 1;
2297
- transform: translateY(0);
2298
- }
2299
- }
2300
- </style>
2301
- </head>
2302
- <body>
2303
- <div class="container">
2304
- <div class="error-header">
2305
- <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
2306
- <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"></path>
2307
- </svg>
2308
- <span>Unhandled Runtime Error</span>
2309
- </div>
2310
-
2311
- <div class="error-badge">SERVER ERROR 500</div>
2312
- ${pathname ? `<div class="route-badge">Route: ${pathname}</div>` : ""}
2313
-
2314
- <div class="error-message">
2315
- <div class="error-message-text">${message}</div>
2316
- </div>
2317
-
2318
- ${isDev && callStackCards ? `
2319
- <div class="stack-section">
2320
- <div class="stack-header">
2321
- <div class="stack-title">
2322
- <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
2323
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
2324
- </svg>
2325
- Call Stack
2326
- </div>
2327
- <div class="frame-counter">${frameCount}</div>
2328
- </div>
2329
- <div id="error-cards">
2330
- ${callStackCards}
2331
- </div>
2332
- ${frameCount > 1 ? `
2333
- <div class="pagination">
2334
- <button class="pagination-btn" id="prev-btn" onclick="changePage(-1)">
2335
- <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-right:4px;">
2336
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
2337
- </svg>
2338
- Previous
2339
- </button>
2340
- <div class="pagination-info">
2341
- <span id="current-page">1</span> / <span id="total-pages">${frameCount}</span>
2342
- </div>
2343
- <button class="pagination-btn" id="next-btn" onclick="changePage(1)">
2344
- Next
2345
- <svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="display:inline;vertical-align:middle;margin-left:4px;">
2346
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
2347
- </svg>
2348
- </button>
2349
- </div>
2350
- ` : ""}
2351
- </div>
2352
- ` : '<div class="no-stack">An error occurred while processing your request. Please check the server logs for more details.</div>'}
2353
-
2354
- <div class="footer">
2355
- <div class="brand">
2356
- <img src="/__velix/logo.webp" alt="Velix" onerror="this.style.display='none'"/>
2357
- <span>Velix v5.0.0</span>
2358
- </div>
2359
- <div class="footer-links">
2360
- <a href="/">Home</a>
2361
- <a href="javascript:location.reload()">Reload</a>
2362
- <a href="https://github.com/velix/velix/tree/main/docs" target="_blank">Documentation</a>
2363
- ${isDev ? '<span style="color:#94A3B8;">Development Mode</span>' : ""}
2364
- </div>
2365
- </div>
2366
- </div>
2367
-
2368
- ${isDev && frameCount > 0 ? `
2369
- <script>
2370
- let currentPage = 1;
2371
- const totalPages = ${frameCount};
2372
- const cards = document.querySelectorAll('.error-card');
2373
- const prevBtn = document.getElementById('prev-btn');
2374
- const nextBtn = document.getElementById('next-btn');
2375
- const currentPageSpan = document.getElementById('current-page');
2376
-
2377
- function showPage(page) {
2378
- cards.forEach((card, index) => {
2379
- card.classList.remove('active');
2380
- if (index === page - 1) {
2381
- card.classList.add('active');
2382
- }
2383
- });
2384
-
2385
- currentPage = page;
2386
- currentPageSpan.textContent = page;
2387
-
2388
- if (prevBtn) prevBtn.disabled = page === 1;
2389
- if (nextBtn) nextBtn.disabled = page === totalPages;
2390
- }
2391
-
2392
- function changePage(direction) {
2393
- const newPage = currentPage + direction;
2394
- if (newPage >= 1 && newPage <= totalPages) {
2395
- showPage(newPage);
2396
- }
2397
- }
2398
-
2399
- // Show first page on load
2400
- showPage(1);
2401
-
2402
- // Keyboard navigation
2403
- document.addEventListener('keydown', (e) => {
2404
- if (e.key === 'ArrowLeft') changePage(-1);
2405
- if (e.key === 'ArrowRight') changePage(1);
2406
- });
2407
- </script>
2408
- ` : ""}
2409
- </body>
2410
- </html>`;
2411
- }
2412
-
2413
- // server/index.ts
2414
- var MIME_TYPES = {
2415
- ".html": "text/html; charset=utf-8",
2416
- ".css": "text/css; charset=utf-8",
2417
- ".js": "application/javascript; charset=utf-8",
2418
- ".mjs": "application/javascript; charset=utf-8",
2419
- ".json": "application/json",
2420
- ".png": "image/png",
2421
- ".jpg": "image/jpeg",
2422
- ".jpeg": "image/jpeg",
2423
- ".gif": "image/gif",
2424
- ".svg": "image/svg+xml",
2425
- ".ico": "image/x-icon",
2426
- ".webp": "image/webp",
2427
- ".woff": "font/woff",
2428
- ".woff2": "font/woff2",
2429
- ".ttf": "font/ttf",
2430
- ".otf": "font/otf",
2431
- ".txt": "text/plain; charset=utf-8",
2432
- ".xml": "application/xml",
2433
- ".mp4": "video/mp4",
2434
- ".webm": "video/webm",
2435
- ".mp3": "audio/mpeg",
2436
- ".pdf": "application/pdf",
2437
- ".map": "application/json"
2438
- };
2439
- async function createServer(options = {}) {
2440
- const projectRoot = options.projectRoot || process.cwd();
2441
- const mode = options.mode || process.env.NODE_ENV || "development";
2442
- const isDev = mode === "development";
2443
- const rawConfig = await loadConfig(projectRoot);
2444
- const config = resolvePaths(rawConfig, projectRoot);
2445
- await loadPlugins(projectRoot, config);
2446
- await pluginManager.runHook(PluginHooks.CONFIG, config);
2447
- const middlewareFns = await loadMiddleware(projectRoot);
2448
- const appDir = config.resolvedAppDir || path8.join(projectRoot, "app");
2449
- const routes = buildRouteTree(appDir);
2450
- await pluginManager.runHook(PluginHooks.ROUTES_LOADED, routes);
2451
- const startTime = Date.now();
2452
- await pluginManager.runHook(PluginHooks.SERVER_START, { config, isDev, projectRoot }, isDev);
2453
- const server = http.createServer(async (req, res) => {
2454
- const requestStart = Date.now();
2455
- const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
2456
- const pathname = url.pathname;
2457
- try {
2458
- if (middlewareFns.length > 0) {
2459
- const middlewareResult = await runMiddleware(req, res, middlewareFns);
2460
- if (!middlewareResult.continue) return;
2461
- }
2462
- await pluginManager.runHook(PluginHooks.REQUEST, req, res);
2463
- if (pathname === "/sitemap.xml" && config.seo.sitemap) {
2464
- const baseUrl = config.app.url || `http://${config.server.host}:${config.server.port}`;
2465
- const sitemap = generateSitemap(routes.appRoutes, baseUrl);
2466
- res.writeHead(200, { "Content-Type": "application/xml" });
2467
- res.end(sitemap);
2468
- return;
2469
- }
2470
- if (pathname === "/robots.txt" && config.seo.robots) {
2471
- const baseUrl = config.app.url || `http://${config.server.host}:${config.server.port}`;
2472
- const robotsTxt = generateRobotsTxt(baseUrl);
2473
- res.writeHead(200, { "Content-Type": "text/plain" });
2474
- res.end(robotsTxt);
2475
- return;
2476
- }
2477
- if (pathname === "/__velix/action" && req.method === "POST") {
2478
- await handleServerAction(req, res);
2479
- return;
2480
- }
2481
- if (pathname === "/__velix/image") {
2482
- const { handleImageOptimization: handleImageOptimization2 } = await Promise.resolve().then(() => (init_image_optimizer(), image_optimizer_exports));
2483
- await handleImageOptimization2(req, res, projectRoot);
2484
- return;
2485
- }
2486
- if (pathname.startsWith("/__velix/")) {
2487
- await serveVelixInternal(pathname, req, res, projectRoot);
2488
- return;
2489
- }
2490
- const publicDir = config.resolvedPublicDir || path8.join(projectRoot, "public");
2491
- if (await serveStaticFile(pathname, publicDir, res, isDev)) {
2492
- if (isDev) logger_default.request(req.method || "GET", pathname, 200, Date.now() - requestStart, { type: "static" });
2493
- return;
2494
- }
2495
- const apiMatch = matchRoute(pathname, routes.api);
2496
- if (apiMatch) {
2497
- await handleApiRoute(apiMatch, req, res, url);
2498
- if (isDev) logger_default.request(req.method || "GET", pathname, res.statusCode, Date.now() - requestStart, { type: "api" });
2499
- return;
2500
- }
2501
- const pageMatch = matchRoute(pathname, routes.appRoutes);
2502
- if (pageMatch) {
2503
- await handlePageRoute(pageMatch, routes, req, res, url, config, isDev, projectRoot);
2504
- if (isDev) logger_default.request(req.method || "GET", pathname, res.statusCode, Date.now() - requestStart, { type: "ssr" });
2505
- return;
2506
- }
2507
- res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
2508
- res.end(generate404Page(pathname));
2509
- if (isDev) logger_default.request(req.method || "GET", pathname, 404, Date.now() - requestStart);
2510
- } catch (error) {
2511
- console.error("Server error:", error);
2512
- if (!res.headersSent) {
2513
- res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
2514
- res.end(generate500Page({
2515
- statusCode: 500,
2516
- title: "Server Error",
2517
- message: error.message || "An unexpected error occurred",
2518
- stack: isDev ? error.stack : void 0,
2519
- isDev,
2520
- pathname
2521
- }));
2522
- }
2523
- }
2524
- });
2525
- const __hmrClients = /* @__PURE__ */ new Set();
2526
- server.__hmrClients = __hmrClients;
2527
- server.broadcastHMR = (msg) => {
2528
- __hmrClients.forEach((c2) => c2.write(`data: ${msg}
2529
-
2530
- `));
2531
- };
2532
- const port = config.server.port;
2533
- const host = config.server.host;
2534
- server.listen(port, host, () => {
2535
- logger_default.serverStart({ port, host, mode, pagesDir: appDir }, startTime);
2536
- if (isDev) {
2537
- routes.appRoutes.forEach((r) => {
2538
- const type = r.path.includes(":") || r.path.includes("*") ? "dynamic" : "static";
2539
- logger_default.route(r.path, type);
2540
- });
2541
- routes.api.forEach((r) => logger_default.route(r.path, "api"));
2542
- logger_default.blank();
2543
- }
2544
- });
2545
- server.on("error", (err) => {
2546
- if (err.code === "EADDRINUSE") {
2547
- logger_default.portInUse(port);
2548
- process.exit(1);
2549
- }
2550
- throw err;
2551
- });
2552
- return {
2553
- server,
2554
- config: rawConfig,
2555
- close: () => server.close()
2556
- };
2557
- }
2558
- async function serveStaticFile(pathname, publicDir, res, isDev = false) {
2559
- const filePath = path8.join(publicDir, pathname);
2560
- if (!filePath.startsWith(publicDir)) return false;
2561
- if (!fs7.existsSync(filePath) || fs7.statSync(filePath).isDirectory()) return false;
2562
- const ext = path8.extname(filePath);
2563
- const contentType = MIME_TYPES[ext] || "application/octet-stream";
2564
- const content = fs7.readFileSync(filePath);
2565
- res.writeHead(200, {
2566
- "Content-Type": contentType,
2567
- "Content-Length": content.length,
2568
- "Cache-Control": isDev ? "no-store, no-cache, must-revalidate" : "public, max-age=31536000, immutable"
2569
- });
2570
- res.end(content);
2571
- return true;
2572
- }
2573
- async function handleApiRoute(route, req, res, url) {
2574
- try {
2575
- const fileUrl = pathToFileURL3(route.filePath).href;
2576
- const mod = await import(`${fileUrl}?t=${Date.now()}`);
2577
- const method = req.method?.toUpperCase() || "GET";
2578
- const handler = mod[method] || mod.default;
2579
- if (!handler) {
2580
- res.writeHead(405, { "Content-Type": "application/json" });
2581
- res.end(JSON.stringify({ error: "Method not allowed" }));
2582
- return;
2583
- }
2584
- let body;
2585
- if (["POST", "PUT", "PATCH"].includes(method)) {
2586
- body = await parseRequestBody(req);
2587
- }
2588
- const request = {
2589
- method,
2590
- url: req.url,
2591
- headers: req.headers,
2592
- params: route.params || {},
2593
- query: Object.fromEntries(url.searchParams),
2594
- body,
2595
- json: () => body
2596
- };
2597
- const response = await handler(request);
2598
- if (response instanceof Response) {
2599
- const headers2 = {};
2600
- response.headers.forEach((v, k) => {
2601
- headers2[k] = v;
2602
- });
2603
- res.writeHead(response.status, headers2);
2604
- const text2 = await response.text();
2605
- res.end(text2);
2606
- } else if (typeof response === "object") {
2607
- res.writeHead(200, { "Content-Type": "application/json" });
2608
- res.end(JSON.stringify(response));
2609
- } else {
2610
- res.writeHead(200, { "Content-Type": "text/plain" });
2611
- res.end(String(response));
2612
- }
2613
- } catch (err) {
2614
- res.writeHead(500, { "Content-Type": "application/json" });
2615
- res.end(JSON.stringify({ error: err.message }));
2616
- }
2617
- }
2618
- async function handleServerAction(req, res) {
2619
- try {
2620
- const body = await parseRequestBody(req);
2621
- if (!body?.actionId || typeof body.actionId !== "string") {
2622
- res.writeHead(400, { "Content-Type": "application/json" });
2623
- res.end(JSON.stringify({ error: "Missing actionId" }));
2624
- return;
2625
- }
2626
- const args = body.args ? deserializeArgs(body.args) : [];
2627
- const result = await executeAction(body.actionId, args);
2628
- res.writeHead(200, { "Content-Type": "application/json" });
2629
- res.end(JSON.stringify(result));
2630
- } catch (err) {
2631
- res.writeHead(500, { "Content-Type": "application/json" });
2632
- res.end(JSON.stringify({ success: false, error: err.message }));
2633
- }
2634
- }
2635
- async function handlePageRoute(route, routes, req, res, url, config, isDev, projectRoot) {
2636
- try {
2637
- const fileUrl = pathToFileURL3(route.filePath).href;
2638
- const mod = await import(`${fileUrl}?t=${Date.now()}`);
2639
- const PageComponent = mod.default;
2640
- let metadata = mod.metadata || mod.generateMetadata?.(route.params) || {};
2641
- let LayoutComponent = ({ children }) => React2.createElement(React2.Fragment, null, children);
2642
- let layoutParams = route.params;
2643
- try {
2644
- const layoutPath = path8.join(path8.dirname(route.filePath), "layout.tsx");
2645
- if (fs7.existsSync(layoutPath)) {
2646
- const layoutMod = await import(`${pathToFileURL3(layoutPath).href}?t=${Date.now()}`);
2647
- if (layoutMod.metadata) {
2648
- metadata = { ...layoutMod.metadata, ...metadata };
2649
- }
2650
- if (layoutMod.default) {
2651
- LayoutComponent = layoutMod.default;
2652
- }
2653
- }
2654
- } catch (e) {
2655
- }
2656
- const baseUrl = config.app.url || `http://${config.server.host}:${config.server.port}`;
2657
- const metaTags = generateMetadataTags({
2658
- ...metadata,
2659
- generator: `Velix v5.0.0`,
2660
- viewport: metadata.viewport || "width=device-width, initial-scale=1"
2661
- }, baseUrl);
2662
- const islands = getRegisteredIslands();
2663
- const hydrationScript = generateAdvancedHydrationScript(islands);
2664
- const devToolsHtml = generateDevToolsHtml(isDev);
2665
- const headInjections = `
2666
- <meta charset="utf-8">
2667
- ${metaTags}
2668
- ${config.favicon ? `<link rel="icon" href="${config.favicon}">` : ""}
2669
- ${config.styles.map((s) => `<link rel="stylesheet" href="${s}">`).join("\n ")}
2670
- `;
2671
- const searchParams = Object.fromEntries(url.searchParams.entries());
2672
- let pageElement = React2.createElement(PageComponent, { params: route.params, searchParams, query: searchParams });
2673
- if (typeof PageComponent === "function") {
2674
- try {
2675
- const result = PageComponent({ params: route.params, searchParams, query: searchParams });
2676
- if (result instanceof Promise) {
2677
- pageElement = await result;
2678
- }
2679
- } catch (e) {
2680
- }
2681
- }
2682
- let layoutElement = React2.createElement(LayoutComponent, { params: layoutParams, searchParams }, pageElement);
2683
- if (typeof LayoutComponent === "function") {
2684
- try {
2685
- const result = LayoutComponent({ params: layoutParams, searchParams, children: pageElement });
2686
- if (result instanceof Promise) {
2687
- layoutElement = await result;
2688
- }
2689
- } catch (e) {
2690
- }
2691
- }
2692
- const ssrContent = renderToString(layoutElement);
2693
- let finalHtml = await pluginManager.runWaterfallHook(PluginHooks.AFTER_RENDER, ssrContent, { route, config, isDev });
2694
- const headInjectionsHtml = `
2695
- ${headInjections}
2696
- `;
2697
- const bodyInjectionsHtml = `
2698
- <div id="__velix-islands"></div>
2699
- ${hydrationScript}${devToolsHtml}
2700
- `;
2701
- if (finalHtml.includes("<html")) {
2702
- const headEnd = finalHtml.lastIndexOf("</head>");
2703
- if (headEnd !== -1) {
2704
- finalHtml = finalHtml.slice(0, headEnd) + headInjectionsHtml + finalHtml.slice(headEnd);
2705
- } else {
2706
- const bodyStart = finalHtml.search(/<body[^>]*>/i);
2707
- if (bodyStart !== -1) {
2708
- finalHtml = finalHtml.slice(0, bodyStart) + `<head>${headInjectionsHtml}</head>` + finalHtml.slice(bodyStart);
2709
- }
2710
- }
2711
- const bodyEnd = finalHtml.lastIndexOf("</body>");
2712
- if (bodyEnd !== -1) {
2713
- finalHtml = finalHtml.slice(0, bodyEnd) + bodyInjectionsHtml + finalHtml.slice(bodyEnd);
2714
- } else {
2715
- finalHtml += bodyInjectionsHtml;
2716
- }
2717
- if (!finalHtml.trim().startsWith("<!DOCTYPE")) {
2718
- finalHtml = "<!DOCTYPE html>\n" + finalHtml;
2719
- }
2720
- } else {
2721
- finalHtml = `<!DOCTYPE html>
2722
- <html lang="en">
2723
- <head>
2724
- ${headInjectionsHtml}
2725
- </head>
2726
- <body>
2727
- <div id="__velix">${ssrContent}</div>
2728
- ${bodyInjectionsHtml}
2729
- </body>
2730
- </html>`;
2731
- }
2732
- res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
2733
- res.end(finalHtml);
2734
- } catch (err) {
2735
- logger_default.error(`Render error: ${route.path}`, err);
2736
- res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
2737
- res.end(generate500Page({
2738
- statusCode: 500,
2739
- title: "Render Error",
2740
- message: err.message || "Failed to render page",
2741
- stack: isDev ? err.stack : void 0,
2742
- isDev,
2743
- pathname: route.path
2744
- }));
2745
- }
2746
- }
2747
- async function serveVelixInternal(pathname, req, res, projectRoot) {
2748
- if (pathname === "/__velix/hmr") {
2749
- res.writeHead(200, {
2750
- "Content-Type": "text/event-stream",
2751
- "Cache-Control": "no-cache",
2752
- "Connection": "keep-alive"
2753
- });
2754
- res.write("data: connected\n\n");
2755
- const interval = setInterval(() => res.write(":heartbeat\n\n"), 3e4);
2756
- if (res.req?.socket?.server?.__hmrClients) res.req.socket.server.__hmrClients.add(res);
2757
- req.on("close", () => {
2758
- clearInterval(interval);
2759
- if (res.req?.socket?.server?.__hmrClients) res.req.socket.server.__hmrClients.delete(res);
2760
- });
2761
- return;
2762
- }
2763
- if (pathname === "/__velix/logo.webp") {
2764
- const __filename2 = fileURLToPath2(import.meta.url);
2765
- const __dirname2 = path8.dirname(__filename2);
2766
- const fallbackPath = path8.join(path8.dirname(pathToFileURL3(__dirname2).pathname), "..", "assets", "logo.webp");
2767
- const logoPath = fs7.existsSync(fallbackPath) ? fallbackPath : path8.join(process.cwd(), "node_modules", "velix", "assets", "logo.webp");
2768
- if (fs7.existsSync(logoPath)) {
2769
- res.writeHead(200, { "Content-Type": "image/webp", "Cache-Control": "public, max-age=31536000, immutable" });
2770
- res.end(fs7.readFileSync(logoPath));
2771
- } else {
2772
- res.writeHead(404);
2773
- res.end();
2774
- }
2775
- return;
2776
- }
2777
- if (pathname.startsWith("/__velix/islands/") && pathname.endsWith(".js")) {
2778
- const componentName = pathname.replace("/__velix/islands/", "").replace(".js", "");
2779
- try {
2780
- const searchDirs = [
2781
- path8.join(projectRoot, "components"),
2782
- path8.join(projectRoot, "app"),
2783
- path8.join(projectRoot, "islands")
2784
- ];
2785
- let componentPath = "";
2786
- for (const dir of searchDirs) {
2787
- if (!fs7.existsSync(dir)) continue;
2788
- const files = fs7.readdirSync(dir, { recursive: true });
2789
- const found = files.find((f) => f.replace(/\\/g, "/").endsWith(`${componentName}.tsx`) || f.replace(/\\/g, "/").endsWith(`${componentName}.jsx`));
2790
- if (found) {
2791
- componentPath = path8.join(dir, found);
2792
- break;
2793
- }
2794
- }
2795
- if (!componentPath) {
2796
- logger_default.error(`Island component not found: ${componentName}`);
2797
- res.writeHead(404);
2798
- res.end("Island component not found");
2799
- return;
2800
- }
2801
- const result = await esbuild.build({
2802
- entryPoints: [componentPath],
2803
- bundle: true,
2804
- format: "esm",
2805
- platform: "browser",
2806
- target: ["es2022"],
2807
- minify: false,
2808
- sourcemap: "inline",
2809
- jsx: "automatic",
2810
- external: ["react", "react-dom"],
2811
- write: false
2812
- });
2813
- res.writeHead(200, { "Content-Type": "application/javascript" });
2814
- res.end(result.outputFiles[0].text);
2815
- return;
2816
- } catch (err) {
2817
- logger_default.error(`Island bundling failed: ${componentName}`, err);
2818
- res.writeHead(500);
2819
- res.end(`console.error("Island bundling failed: ${err.message}");`);
2820
- return;
2821
- }
2822
- }
2823
- if (pathname === "/__velix/react.js" || pathname === "/__velix/react-dom-client.js") {
2824
- const dep = pathname === "/__velix/react.js" ? "react" : "react-dom/client";
2825
- try {
2826
- const result = await esbuild.build({
2827
- entryPoints: [dep],
2828
- bundle: true,
2829
- format: "esm",
2830
- platform: "browser",
2831
- target: ["es2022"],
2832
- minify: true,
2833
- write: false
2834
- });
2835
- res.writeHead(200, { "Content-Type": "application/javascript", "Cache-Control": "public, max-age=31536000, immutable" });
2836
- res.end(result.outputFiles[0].text);
2837
- return;
2838
- } catch (err) {
2839
- res.writeHead(500);
2840
- res.end();
2841
- return;
2842
- }
2843
- }
2844
- res.writeHead(404);
2845
- res.end("Not found");
2846
- }
2847
- function parseRequestBody(req) {
2848
- return new Promise((resolve, reject) => {
2849
- let body = "";
2850
- req.on("data", (chunk) => {
2851
- body += chunk;
2852
- });
2853
- req.on("end", () => {
2854
- try {
2855
- const ct = req.headers["content-type"] || "";
2856
- if (ct.includes("application/json")) resolve(JSON.parse(body));
2857
- else resolve(body);
2858
- } catch {
2859
- resolve(body);
2860
- }
2861
- });
2862
- req.on("error", reject);
2863
- });
2864
- }
1
+ export { LayoutContext, Link, RequestContext, RouteContext, createRequestContext, hydrate, router, useParams, usePathname, useQuery, useRequest, useRouter } from './chunk-INOZP2VD.js';
2
+ export { build } from './chunk-7SGDYOVZ.js';
3
+ export { NotFoundError, PluginHooks, PluginManager, RedirectError, bindArgs, callServerAction, composeMiddleware, cookies, createServer, definePlugin, deserializeArgs, executeAction, formAction, generateJsonLd, generateMetadataTags, generateRobotsTxt, generateSitemap, getAction, getMethod, getPathname, headers, html, isMethod, json, jsonLd, loadMiddleware, loadPlugins, mergeMetadata, middlewares, notFound, parseFormData, parseJson, parseSearchParams, pluginManager, redirect, registerAction, runMiddleware, serverAction, tailwindPlugin, text, useActionContext, useActionState, useFormStatus, useOptimistic, useVelixAction } from './chunk-NVTZ6HRX.js';
4
+ export { buildRouteTree, cleanDir, copyDir, debounce, ensureDir, escapeHtml, findFiles, findRouteLayouts, formatBytes, formatTime, generateHash, isClientComponent, isIsland as isIslandComponent, isServerComponent, logger, matchRoute, sleep } from './chunk-SPIGTNT7.js';
5
+ export { VelixConfigSchema, defaultConfig, defineConfig, loadConfig, resolvePaths } from './chunk-F24Q2MX3.js';
6
+ export { Island, LoadStrategy, createIsland, createLazyIsland, generateAdvancedHydrationScript, generateHydrationScript, getRegisteredIslands } from './chunk-OIZNYND3.js';
7
+ import { forwardRef, useState, use, useOptimistic } from 'react';
8
+ export { use } from 'react';
9
+ import 'react-dom';
10
+ import { jsx } from 'react/jsx-runtime';
2865
11
 
2866
12
  // actions/revalidation.ts
2867
- init_esm_shims();
2868
13
  var CacheManager = class {
2869
14
  cache = /* @__PURE__ */ new Map();
2870
15
  tagIndex = /* @__PURE__ */ new Map();
2871
- set(path10, data, tags = []) {
16
+ set(path, data, tags = []) {
2872
17
  const entry = {
2873
- path: path10,
18
+ path,
2874
19
  tags: new Set(tags),
2875
20
  timestamp: Date.now(),
2876
21
  data
2877
22
  };
2878
- this.cache.set(path10, entry);
23
+ this.cache.set(path, entry);
2879
24
  tags.forEach((tag) => {
2880
25
  if (!this.tagIndex.has(tag)) {
2881
26
  this.tagIndex.set(tag, /* @__PURE__ */ new Set());
2882
27
  }
2883
- this.tagIndex.get(tag).add(path10);
28
+ this.tagIndex.get(tag).add(path);
2884
29
  });
2885
30
  }
2886
- get(path10) {
2887
- const entry = this.cache.get(path10);
31
+ get(path) {
32
+ const entry = this.cache.get(path);
2888
33
  return entry ? entry.data : null;
2889
34
  }
2890
- revalidatePath(path10) {
2891
- this.cache.delete(path10);
35
+ revalidatePath(path) {
36
+ this.cache.delete(path);
2892
37
  }
2893
38
  revalidateTag(tag) {
2894
39
  const paths = this.tagIndex.get(tag);
2895
40
  if (paths) {
2896
- paths.forEach((path10) => this.cache.delete(path10));
41
+ paths.forEach((path) => this.cache.delete(path));
2897
42
  this.tagIndex.delete(tag);
2898
43
  }
2899
44
  }
@@ -2901,17 +46,17 @@ var CacheManager = class {
2901
46
  this.cache.clear();
2902
47
  this.tagIndex.clear();
2903
48
  }
2904
- has(path10) {
2905
- return this.cache.has(path10);
49
+ has(path) {
50
+ return this.cache.has(path);
2906
51
  }
2907
52
  };
2908
53
  var cacheManager = new CacheManager();
2909
- function revalidatePath(path10, type = "path") {
2910
- cacheManager.revalidatePath(path10);
54
+ function revalidatePath(path, type = "path") {
55
+ cacheManager.revalidatePath(path);
2911
56
  if (typeof global !== "undefined" && global.__VELIX_HMR_SERVER__) {
2912
57
  global.__VELIX_HMR_SERVER__.broadcast(JSON.stringify({
2913
58
  type: "revalidate",
2914
- path: path10,
59
+ path,
2915
60
  revalidationType: type
2916
61
  }));
2917
62
  }
@@ -2941,193 +86,15 @@ function unstable_cache(fn, keys, options) {
2941
86
  return result;
2942
87
  };
2943
88
  }
2944
-
2945
- // hooks/index.ts
2946
- init_esm_shims();
2947
- import { useActionState as useActionState2, useOptimistic as useOptimistic2, use } from "react";
2948
- import { useFormStatus as useFormStatus2 } from "react-dom";
2949
-
2950
- // context.ts
2951
- init_esm_shims();
2952
- import React3 from "react";
2953
- var RequestContext = React3.createContext(null);
2954
- var RouteContext = React3.createContext(null);
2955
- var LayoutContext = React3.createContext(null);
2956
- function createRequestContext(req, res, params = {}, query = {}) {
2957
- return {
2958
- req,
2959
- res,
2960
- params,
2961
- query,
2962
- url: req.url,
2963
- method: req.method,
2964
- headers: req.headers,
2965
- cookies: parseCookies(req.headers.cookie || "")
2966
- };
2967
- }
2968
- function parseCookies(cookieHeader) {
2969
- const cookies2 = {};
2970
- if (!cookieHeader) return cookies2;
2971
- cookieHeader.split(";").forEach((cookie) => {
2972
- const [name, ...rest] = cookie.split("=");
2973
- if (name) cookies2[name.trim()] = rest.join("=").trim();
2974
- });
2975
- return cookies2;
2976
- }
2977
- function useRequest() {
2978
- const context = React3.useContext(RequestContext);
2979
- if (!context) throw new Error("useRequest must be used within a RequestContext provider");
2980
- return context;
2981
- }
2982
- function useParams() {
2983
- const context = React3.useContext(RouteContext);
2984
- return context?.params || {};
2985
- }
2986
- function useQuery() {
2987
- const context = React3.useContext(RouteContext);
2988
- return context?.query || {};
2989
- }
2990
- function usePathname() {
2991
- const context = React3.useContext(RouteContext);
2992
- return context?.pathname || "/";
2993
- }
2994
-
2995
- // hooks/index.ts
2996
- import { use as use2, useOptimistic as useOptimisticReact } from "react";
2997
89
  function useAsyncData(promise) {
2998
- return use2(promise);
90
+ return use(promise);
2999
91
  }
3000
- function useOptimisticMutation(currentState2, updateFn) {
3001
- return useOptimisticReact(currentState2, updateFn);
92
+ function useOptimisticMutation(currentState, updateFn) {
93
+ return useOptimistic(currentState, updateFn);
3002
94
  }
3003
95
  function preloadResource(fetcher) {
3004
96
  return fetcher();
3005
97
  }
3006
-
3007
- // client/index.ts
3008
- init_esm_shims();
3009
- import React4 from "react";
3010
- var listeners = /* @__PURE__ */ new Set();
3011
- var currentState = {
3012
- pathname: typeof window !== "undefined" ? window.location.pathname : "/",
3013
- query: {},
3014
- params: {}
3015
- };
3016
- function notify() {
3017
- listeners.forEach((fn) => fn());
3018
- }
3019
- var router = {
3020
- push(url) {
3021
- if (typeof window === "undefined") return;
3022
- window.history.pushState({}, "", url);
3023
- currentState = { ...currentState, pathname: url.split("?")[0] };
3024
- notify();
3025
- },
3026
- replace(url) {
3027
- if (typeof window === "undefined") return;
3028
- window.history.replaceState({}, "", url);
3029
- currentState = { ...currentState, pathname: url.split("?")[0] };
3030
- notify();
3031
- },
3032
- back() {
3033
- if (typeof window === "undefined") return;
3034
- window.history.back();
3035
- },
3036
- forward() {
3037
- if (typeof window === "undefined") return;
3038
- window.history.forward();
3039
- },
3040
- refresh() {
3041
- if (typeof window === "undefined") return;
3042
- window.location.reload();
3043
- },
3044
- prefetch(_url) {
3045
- },
3046
- subscribe(listener) {
3047
- listeners.add(listener);
3048
- return () => listeners.delete(listener);
3049
- },
3050
- getState() {
3051
- return currentState;
3052
- }
3053
- };
3054
- function useRouter() {
3055
- return router;
3056
- }
3057
- if (typeof window !== "undefined") {
3058
- window.addEventListener("popstate", () => {
3059
- currentState = { ...currentState, pathname: window.location.pathname };
3060
- notify();
3061
- });
3062
- }
3063
- function Link({ href, prefetch = false, replace: shouldReplace = false, scroll = true, shallow = false, children, onClick, onMouseEnter, ...rest }) {
3064
- const linkRef = React4.useRef(null);
3065
- const [isPrefetched, setIsPrefetched] = React4.useState(false);
3066
- const handleMouseEnter = (e) => {
3067
- if (prefetch === "hover" && !isPrefetched) {
3068
- router.prefetch(href);
3069
- setIsPrefetched(true);
3070
- }
3071
- onMouseEnter?.(e);
3072
- };
3073
- React4.useEffect(() => {
3074
- if (prefetch === "visible" && linkRef.current && !isPrefetched) {
3075
- const observer = new IntersectionObserver(
3076
- (entries) => {
3077
- if (entries[0].isIntersecting) {
3078
- router.prefetch(href);
3079
- setIsPrefetched(true);
3080
- observer.disconnect();
3081
- }
3082
- },
3083
- { rootMargin: "100px" }
3084
- );
3085
- observer.observe(linkRef.current);
3086
- return () => observer.disconnect();
3087
- }
3088
- }, [href, prefetch, isPrefetched]);
3089
- React4.useEffect(() => {
3090
- if (prefetch === true && !isPrefetched) {
3091
- router.prefetch(href);
3092
- setIsPrefetched(true);
3093
- }
3094
- }, [href, prefetch, isPrefetched]);
3095
- const handleClick = (e) => {
3096
- if (href.startsWith("http") || href.startsWith("//") || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || e.button !== 0) {
3097
- onClick?.(e);
3098
- return;
3099
- }
3100
- e.preventDefault();
3101
- onClick?.(e);
3102
- if (typeof window !== "undefined" && window.__VELIX_DEV_TOOLS__) {
3103
- window.__VELIX_DEV_TOOLS__.setStatus("navigating");
3104
- }
3105
- if (shouldReplace) {
3106
- router.replace(href);
3107
- } else {
3108
- router.push(href);
3109
- }
3110
- if (scroll) {
3111
- window.scrollTo({ top: 0, behavior: "smooth" });
3112
- }
3113
- };
3114
- return React4.createElement("a", { ref: linkRef, href, onClick: handleClick, onMouseEnter: handleMouseEnter, ...rest }, children);
3115
- }
3116
- async function hydrate() {
3117
- if (typeof window === "undefined") return;
3118
- const { hydrateRoot } = await import("react-dom/client");
3119
- const rootElement = document.getElementById("__velix");
3120
- if (!rootElement) {
3121
- console.warn("[Velix] No #__velix element found for hydration");
3122
- return;
3123
- }
3124
- console.log("[Velix] Hydration complete");
3125
- }
3126
-
3127
- // components/Image.tsx
3128
- init_esm_shims();
3129
- import { forwardRef, useState } from "react";
3130
- import { jsx } from "react/jsx-runtime";
3131
98
  var defaultWidths = [640, 750, 828, 1080, 1200, 1920, 2048, 3840];
3132
99
  var defaultQuality = 75;
3133
100
  var Image = forwardRef(({
@@ -3176,226 +143,6 @@ var Image = forwardRef(({
3176
143
  });
3177
144
  Image.displayName = "Image";
3178
145
 
3179
- // build/index.ts
3180
- init_esm_shims();
3181
- import esbuild2 from "esbuild";
3182
- import fs8 from "fs";
3183
- import path9 from "path";
3184
- async function build(options = {}) {
3185
- const projectRoot = options.projectRoot || process.cwd();
3186
- const config = await loadConfig(projectRoot);
3187
- const resolved = resolvePaths(config, projectRoot);
3188
- const outDir = options.outDir || resolved.resolvedOutDir;
3189
- const startTime = Date.now();
3190
- logger_default.logo();
3191
- logger_default.info("Building for production...");
3192
- logger_default.blank();
3193
- cleanDir(outDir);
3194
- const appDir = resolved.resolvedAppDir;
3195
- const routes = buildRouteTree(appDir);
3196
- const sourceFiles = findFiles(appDir, /\.(tsx?|jsx?)$/);
3197
- if (sourceFiles.length === 0) {
3198
- logger_default.warn("No source files found in app/ directory");
3199
- return;
3200
- }
3201
- try {
3202
- const serverOutDir = path9.join(outDir, "server");
3203
- ensureDir(serverOutDir);
3204
- await esbuild2.build({
3205
- entryPoints: sourceFiles,
3206
- outdir: serverOutDir,
3207
- bundle: false,
3208
- format: "esm",
3209
- platform: "node",
3210
- target: config.build.target,
3211
- minify: options.minify ?? config.build.minify,
3212
- sourcemap: options.sourcemap ?? config.build.sourcemap,
3213
- jsx: "automatic",
3214
- logLevel: "silent"
3215
- });
3216
- logger_default.success("Server bundle built");
3217
- } catch (err) {
3218
- logger_default.error("Server build failed", err);
3219
- process.exit(1);
3220
- }
3221
- try {
3222
- const clientOutDir = path9.join(outDir, "client");
3223
- ensureDir(clientOutDir);
3224
- const clientFiles = sourceFiles.filter((f) => {
3225
- const content = fs8.readFileSync(f, "utf-8");
3226
- const firstLine = content.split("\n")[0]?.trim();
3227
- return firstLine === "'use client'" || firstLine === '"use client"' || firstLine === "'use island'" || firstLine === '"use island"';
3228
- });
3229
- if (clientFiles.length > 0) {
3230
- await esbuild2.build({
3231
- entryPoints: clientFiles,
3232
- outdir: clientOutDir,
3233
- bundle: true,
3234
- format: "esm",
3235
- platform: "browser",
3236
- target: ["es2022"],
3237
- minify: options.minify ?? config.build.minify,
3238
- sourcemap: options.sourcemap ?? config.build.sourcemap,
3239
- splitting: config.build.splitting,
3240
- jsx: "automatic",
3241
- logLevel: "silent",
3242
- external: ["react", "react-dom"]
3243
- });
3244
- logger_default.success(`Client bundle built (${clientFiles.length} components)`);
3245
- }
3246
- } catch (err) {
3247
- logger_default.error("Client build failed", err);
3248
- process.exit(1);
3249
- }
3250
- const publicDir = resolved.resolvedPublicDir;
3251
- if (fs8.existsSync(publicDir)) {
3252
- const publicOutDir = path9.join(outDir, "public");
3253
- ensureDir(publicOutDir);
3254
- copyDirRecursive(publicDir, publicOutDir);
3255
- logger_default.success("Static assets copied");
3256
- }
3257
- const manifest = {
3258
- version: "5.0.0",
3259
- buildTime: (/* @__PURE__ */ new Date()).toISOString(),
3260
- routes: routes.appRoutes.map((r) => ({
3261
- path: r.path,
3262
- type: r.path.includes(":") ? "dynamic" : "static"
3263
- })),
3264
- api: routes.api.map((r) => ({ path: r.path }))
3265
- };
3266
- fs8.writeFileSync(path9.join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2));
3267
- const elapsed = Date.now() - startTime;
3268
- const totalSize = getDirSize(outDir);
3269
- logger_default.blank();
3270
- logger_default.divider();
3271
- logger_default.blank();
3272
- routes.appRoutes.forEach((r) => {
3273
- const type = r.path.includes(":") || r.path.includes("*") ? "dynamic" : "static";
3274
- logger_default.route(r.path, type);
3275
- });
3276
- routes.api.forEach((r) => logger_default.route(r.path, "api"));
3277
- logger_default.blank();
3278
- logger_default.build({ time: elapsed });
3279
- logger_default.info(`Output: ${outDir} (${formatBytes(totalSize)})`);
3280
- logger_default.blank();
3281
- }
3282
- function copyDirRecursive(src, dest) {
3283
- ensureDir(dest);
3284
- const entries = fs8.readdirSync(src, { withFileTypes: true });
3285
- for (const entry of entries) {
3286
- const srcPath = path9.join(src, entry.name);
3287
- const destPath = path9.join(dest, entry.name);
3288
- if (entry.isDirectory()) copyDirRecursive(srcPath, destPath);
3289
- else fs8.copyFileSync(srcPath, destPath);
3290
- }
3291
- }
3292
- function getDirSize(dir) {
3293
- let size = 0;
3294
- if (!fs8.existsSync(dir)) return size;
3295
- const entries = fs8.readdirSync(dir, { withFileTypes: true });
3296
- for (const entry of entries) {
3297
- const fullPath = path9.join(dir, entry.name);
3298
- if (entry.isDirectory()) size += getDirSize(fullPath);
3299
- else size += fs8.statSync(fullPath).size;
3300
- }
3301
- return size;
3302
- }
3303
- export {
3304
- Image,
3305
- Island,
3306
- LayoutContext,
3307
- Link,
3308
- LoadStrategy,
3309
- NotFoundError,
3310
- PluginHooks,
3311
- PluginManager,
3312
- RedirectError,
3313
- RequestContext,
3314
- RouteContext,
3315
- VelixConfigSchema,
3316
- bindArgs,
3317
- build,
3318
- buildRouteTree,
3319
- cacheManager,
3320
- callServerAction,
3321
- cleanDir,
3322
- composeMiddleware,
3323
- cookies,
3324
- copyDir,
3325
- createIsland,
3326
- createLazyIsland,
3327
- createRequestContext,
3328
- createServer,
3329
- debounce,
3330
- defaultConfig,
3331
- defineConfig,
3332
- definePlugin,
3333
- deserializeArgs,
3334
- ensureDir,
3335
- escapeHtml,
3336
- executeAction,
3337
- findFiles,
3338
- findRouteLayouts,
3339
- formAction,
3340
- formatBytes,
3341
- formatTime,
3342
- generateAdvancedHydrationScript,
3343
- generateHash,
3344
- generateHydrationScript,
3345
- generateJsonLd,
3346
- generateMetadataTags,
3347
- generateRobotsTxt,
3348
- generateSitemap,
3349
- getAction,
3350
- getMethod,
3351
- getPathname,
3352
- getRegisteredIslands,
3353
- headers,
3354
- html,
3355
- hydrate,
3356
- isClientComponent,
3357
- isIsland as isIslandComponent,
3358
- isMethod,
3359
- isServerComponent,
3360
- json,
3361
- jsonLd,
3362
- loadConfig,
3363
- loadMiddleware,
3364
- loadPlugins,
3365
- logger,
3366
- matchRoute,
3367
- mergeMetadata,
3368
- middlewares,
3369
- notFound,
3370
- parseFormData,
3371
- parseJson,
3372
- parseSearchParams,
3373
- pluginManager,
3374
- preloadResource,
3375
- redirect,
3376
- registerAction,
3377
- resolvePaths,
3378
- revalidatePath,
3379
- revalidateTag,
3380
- router,
3381
- runMiddleware,
3382
- serverAction,
3383
- sleep,
3384
- tailwindPlugin,
3385
- text,
3386
- unstable_cache,
3387
- use,
3388
- useActionContext,
3389
- useActionState,
3390
- useAsyncData,
3391
- useFormStatus,
3392
- useOptimistic,
3393
- useOptimisticMutation,
3394
- useParams,
3395
- usePathname,
3396
- useQuery,
3397
- useRequest,
3398
- useRouter,
3399
- useVelixAction
3400
- };
146
+ export { Image, cacheManager, preloadResource, revalidatePath, revalidateTag, unstable_cache, useAsyncData, useOptimisticMutation };
147
+ //# sourceMappingURL=index.js.map
3401
148
  //# sourceMappingURL=index.js.map