@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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../context.ts","../../client/index.ts"],"sourcesContent":["/**\n * Velix v5 Context System\n * Provides request context and shared state for SSR\n */\n\nimport React from 'react';\n\n// Route context type\ninterface RouteContextType {\n params: Record<string, string>;\n query: Record<string, string>;\n pathname: string;\n}\n\n// Server-side request context\nexport const RequestContext = React.createContext<any>(null);\n\n// Route context for nested routes\nexport const RouteContext = React.createContext<RouteContextType | null>(null);\n\n// Layout context\nexport const LayoutContext = React.createContext<any>(null);\n\n/**\n * Creates a request context value\n */\nexport function createRequestContext(\n req: import('http').IncomingMessage,\n res: import('http').ServerResponse,\n params: Record<string, string> = {},\n query: Record<string, string> = {}\n) {\n return {\n req,\n res,\n params,\n query,\n url: req.url,\n method: req.method,\n headers: req.headers,\n cookies: parseCookies(req.headers.cookie || '')\n };\n}\n\n/**\n * Parse cookies from header string\n */\nfunction parseCookies(cookieHeader: string): Record<string, string> {\n const cookies: Record<string, string> = {};\n if (!cookieHeader) return cookies;\n cookieHeader.split(';').forEach((cookie: string) => {\n const [name, ...rest] = cookie.split('=');\n if (name) cookies[name.trim()] = rest.join('=').trim();\n });\n return cookies;\n}\n\n/**\n * Hook to access request context (server-side only)\n */\nexport function useRequest() {\n const context = React.useContext(RequestContext);\n if (!context) throw new Error('useRequest must be used within a RequestContext provider');\n return context;\n}\n\n/**\n * Hook to access route params\n */\nexport function useParams() {\n const context = React.useContext(RouteContext);\n return context?.params || {};\n}\n\n/**\n * Hook to access query parameters\n */\nexport function useQuery() {\n const context = React.useContext(RouteContext);\n return context?.query || {};\n}\n\n/**\n * Hook to access current pathname\n */\nexport function usePathname() {\n const context = React.useContext(RouteContext);\n return context?.pathname || '/';\n}\n","/**\n * Velix v5 Client Module\n * Client-side navigation, hydration, and the Link component.\n */\n\nexport { useParams, useQuery, usePathname } from '../context.js';\n\n// ============================================================================\n// Client-side Router\n// ============================================================================\n\ninterface RouterState {\n pathname: string;\n query: Record<string, string>;\n params: Record<string, string>;\n}\n\nconst listeners = new Set<() => void>();\nlet currentState: RouterState = {\n pathname: typeof window !== 'undefined' ? window.location.pathname : '/',\n query: {},\n params: {},\n};\n\nfunction notify() {\n listeners.forEach(fn => fn());\n}\n\nexport const router = {\n push(url: string) {\n if (typeof window === 'undefined') return;\n window.history.pushState({}, '', url);\n currentState = { ...currentState, pathname: url.split('?')[0] };\n notify();\n },\n\n replace(url: string) {\n if (typeof window === 'undefined') return;\n window.history.replaceState({}, '', url);\n currentState = { ...currentState, pathname: url.split('?')[0] };\n notify();\n },\n\n back() {\n if (typeof window === 'undefined') return;\n window.history.back();\n },\n\n forward() {\n if (typeof window === 'undefined') return;\n window.history.forward();\n },\n\n refresh() {\n if (typeof window === 'undefined') return;\n window.location.reload();\n },\n\n prefetch(_url: string) {\n // Future: implement prefetching\n },\n\n subscribe(listener: () => void) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n getState(): RouterState {\n return currentState;\n },\n};\n\n/**\n * Hook to access the Velix client router\n */\nexport function useRouter() {\n return router;\n}\n\n// Listen for popstate (browser back/forward)\nif (typeof window !== 'undefined') {\n window.addEventListener('popstate', () => {\n currentState = { ...currentState, pathname: window.location.pathname };\n notify();\n });\n}\n\n// ============================================================================\n// Link Component\n// ============================================================================\n\nimport React from 'react';\n\ninterface LinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string;\n prefetch?: boolean | 'hover' | 'visible';\n replace?: boolean;\n scroll?: boolean;\n shallow?: boolean;\n children: React.ReactNode;\n}\n\n/**\n * Client-side navigation link component\n *\n * @example\n * ```tsx\n * import { Link } from 'velix/client';\n *\n * <Link href=\"/dashboard\">Dashboard</Link>\n * <Link href=\"/blog\" prefetch>Blog</Link>\n * ```\n */\nexport function Link({ href, prefetch = false, replace: shouldReplace = false, scroll = true, shallow = false, children, onClick, onMouseEnter, ...rest }: LinkProps) {\n const linkRef = React.useRef<HTMLAnchorElement>(null);\n const [isPrefetched, setIsPrefetched] = React.useState(false);\n\n // Prefetch on hover\n const handleMouseEnter = (e: React.MouseEvent<HTMLAnchorElement>) => {\n if (prefetch === 'hover' && !isPrefetched) {\n router.prefetch(href);\n setIsPrefetched(true);\n }\n onMouseEnter?.(e);\n };\n\n // Prefetch when visible\n React.useEffect(() => {\n if (prefetch === 'visible' && linkRef.current && !isPrefetched) {\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting) {\n router.prefetch(href);\n setIsPrefetched(true);\n observer.disconnect();\n }\n },\n { rootMargin: '100px' }\n );\n observer.observe(linkRef.current);\n return () => observer.disconnect();\n }\n }, [href, prefetch, isPrefetched]);\n\n // Prefetch immediately\n React.useEffect(() => {\n if (prefetch === true && !isPrefetched) {\n router.prefetch(href);\n setIsPrefetched(true);\n }\n }, [href, prefetch, isPrefetched]);\n\n const handleClick = (e: React.MouseEvent<HTMLAnchorElement>) => {\n // Allow default behavior for external links, ctrl/meta clicks, etc.\n if (\n href.startsWith('http') || href.startsWith('//') ||\n e.ctrlKey || e.metaKey || e.shiftKey || e.altKey ||\n e.button !== 0\n ) {\n onClick?.(e);\n return;\n }\n\n e.preventDefault();\n onClick?.(e);\n\n // Notify DevTools of navigation start\n if (typeof window !== 'undefined' && (window as any).__VELIX_DEV_TOOLS__) {\n (window as any).__VELIX_DEV_TOOLS__.setStatus('navigating');\n }\n\n if (shouldReplace) {\n router.replace(href);\n } else {\n router.push(href);\n }\n\n if (scroll) {\n window.scrollTo({ top: 0, behavior: 'smooth' });\n }\n };\n\n return React.createElement('a', { ref: linkRef, href, onClick: handleClick, onMouseEnter: handleMouseEnter, ...rest }, children);\n}\n\n// ============================================================================\n// Hydration\n// ============================================================================\n\n/**\n * Client entry point for hydrating the app\n */\nexport async function hydrate() {\n if (typeof window === 'undefined') return;\n\n const { hydrateRoot } = await import('react-dom/client');\n const rootElement = document.getElementById('__velix');\n\n if (!rootElement) {\n console.warn('[Velix] No #__velix element found for hydration');\n return;\n }\n\n // Minimal hydration — full implementation is done per-island\n console.log('[Velix] Hydration complete');\n}\n\nexport default { Link, useRouter, router, hydrate };\n"],"mappings":"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","names":["React"]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/dist/config.js CHANGED
@@ -1,129 +1,3 @@
1
- // config.ts
2
- import fs from "fs";
3
- import path from "path";
4
- import { pathToFileURL } from "url";
5
- import { z } from "zod";
6
- import pc from "picocolors";
7
- var AppConfigSchema = z.object({
8
- name: z.string().default("Velix App"),
9
- url: z.string().url().optional()
10
- }).default({});
11
- var ServerConfigSchema = z.object({
12
- port: z.number().min(1).max(65535).default(3e3),
13
- host: z.string().default("localhost")
14
- }).default({});
15
- var RoutingConfigSchema = z.object({
16
- trailingSlash: z.boolean().default(false)
17
- }).default({});
18
- var SEOConfigSchema = z.object({
19
- sitemap: z.boolean().default(true),
20
- robots: z.boolean().default(true),
21
- openGraph: z.boolean().default(true)
22
- }).default({});
23
- var BuildConfigSchema = z.object({
24
- target: z.string().default("es2022"),
25
- minify: z.boolean().default(true),
26
- sourcemap: z.boolean().default(true),
27
- splitting: z.boolean().default(true),
28
- outDir: z.string().default(".velix")
29
- }).default({});
30
- var ExperimentalConfigSchema = z.object({
31
- islands: z.boolean().default(true),
32
- streaming: z.boolean().default(true)
33
- }).default({});
34
- var PluginSchema = z.union([
35
- z.string(),
36
- z.object({
37
- name: z.string()
38
- }).passthrough()
39
- ]);
40
- var VelixConfigSchema = z.object({
41
- // App identity
42
- app: AppConfigSchema,
43
- // DevTools toggle
44
- devtools: z.boolean().default(true),
45
- // Server options
46
- server: ServerConfigSchema,
47
- // Routing options
48
- routing: RoutingConfigSchema,
49
- // SEO configuration
50
- seo: SEOConfigSchema,
51
- // Build options
52
- build: BuildConfigSchema,
53
- // Experimental features
54
- experimental: ExperimentalConfigSchema,
55
- // Plugins
56
- plugins: z.array(PluginSchema).default([]),
57
- // Directories (resolved automatically)
58
- appDir: z.string().default("app"),
59
- publicDir: z.string().default("public"),
60
- // Stylesheets
61
- styles: z.array(z.string()).default([]),
62
- // Favicon
63
- favicon: z.string().nullable().default(null)
64
- });
65
- var defaultConfig = VelixConfigSchema.parse({});
66
- function defineConfig(config) {
67
- return config;
68
- }
69
- async function loadConfig(projectRoot) {
70
- const configPathTs = path.join(projectRoot, "velix.config.ts");
71
- const configPathJs = path.join(projectRoot, "velix.config.js");
72
- const configPathLegacyTs = path.join(projectRoot, "flexireact.config.ts");
73
- const configPathLegacyJs = path.join(projectRoot, "flexireact.config.js");
74
- let configPath = null;
75
- if (fs.existsSync(configPathTs)) configPath = configPathTs;
76
- else if (fs.existsSync(configPathJs)) configPath = configPathJs;
77
- else if (fs.existsSync(configPathLegacyTs)) configPath = configPathLegacyTs;
78
- else if (fs.existsSync(configPathLegacyJs)) configPath = configPathLegacyJs;
79
- let userConfig = {};
80
- if (configPath) {
81
- try {
82
- const configUrl = pathToFileURL(configPath).href;
83
- const module = await import(`${configUrl}?t=${Date.now()}`);
84
- userConfig = module.default || module;
85
- } catch (error) {
86
- console.warn(pc.yellow(`\u26A0 Failed to load config: ${error.message}`));
87
- }
88
- }
89
- const merged = deepMerge(defaultConfig, userConfig);
90
- try {
91
- return VelixConfigSchema.parse(merged);
92
- } catch (err) {
93
- if (err instanceof z.ZodError) {
94
- console.error(pc.red("\u2716 Configuration validation failed:"));
95
- for (const issue of err.issues) {
96
- console.error(pc.dim(` - ${issue.path.join(".")}: ${issue.message}`));
97
- }
98
- process.exit(1);
99
- }
100
- throw err;
101
- }
102
- }
103
- function resolvePaths(config, projectRoot) {
104
- return {
105
- ...config,
106
- resolvedAppDir: path.resolve(projectRoot, config.appDir),
107
- resolvedPublicDir: path.resolve(projectRoot, config.publicDir),
108
- resolvedOutDir: path.resolve(projectRoot, config.build.outDir)
109
- };
110
- }
111
- function deepMerge(target, source) {
112
- const result = { ...target };
113
- for (const key in source) {
114
- if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
115
- result[key] = deepMerge(target[key] || {}, source[key]);
116
- } else {
117
- result[key] = source[key];
118
- }
119
- }
120
- return result;
121
- }
122
- export {
123
- VelixConfigSchema,
124
- defaultConfig,
125
- defineConfig,
126
- loadConfig,
127
- resolvePaths
128
- };
1
+ export { VelixConfigSchema, defaultConfig, defineConfig, loadConfig, resolvePaths } from './chunk-F24Q2MX3.js';
2
+ //# sourceMappingURL=config.js.map
129
3
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../config.ts"],"sourcesContent":["/**\n * Velix v5 Configuration System\n * Handles loading, validation, and merging of velix.config.ts\n */\n\nimport fs from 'fs';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\nimport { z } from 'zod';\nimport pc from 'picocolors';\n\n// ============================================================================\n// Configuration Schema (Zod validation)\n// ============================================================================\n\nconst AppConfigSchema = z.object({\n name: z.string().default('Velix App'),\n url: z.string().url().optional(),\n}).default({});\n\nconst ServerConfigSchema = z.object({\n port: z.number().min(1).max(65535).default(3000),\n host: z.string().default('localhost'),\n}).default({});\n\nconst RoutingConfigSchema = z.object({\n trailingSlash: z.boolean().default(false),\n}).default({});\n\nconst SEOConfigSchema = z.object({\n sitemap: z.boolean().default(true),\n robots: z.boolean().default(true),\n openGraph: z.boolean().default(true),\n}).default({});\n\nconst BuildConfigSchema = z.object({\n target: z.string().default('es2022'),\n minify: z.boolean().default(true),\n sourcemap: z.boolean().default(true),\n splitting: z.boolean().default(true),\n outDir: z.string().default('.velix'),\n}).default({});\n\nconst ExperimentalConfigSchema = z.object({\n islands: z.boolean().default(true),\n streaming: z.boolean().default(true),\n}).default({});\n\nconst PluginSchema = z.union([\n z.string(),\n z.object({\n name: z.string()\n }).passthrough(),\n]);\n\nexport const VelixConfigSchema = z.object({\n // App identity\n app: AppConfigSchema,\n\n // DevTools toggle\n devtools: z.boolean().default(true),\n\n // Server options\n server: ServerConfigSchema,\n\n // Routing options\n routing: RoutingConfigSchema,\n\n // SEO configuration\n seo: SEOConfigSchema,\n\n // Build options\n build: BuildConfigSchema,\n\n // Experimental features\n experimental: ExperimentalConfigSchema,\n\n // Plugins\n plugins: z.array(PluginSchema).default([]),\n\n // Directories (resolved automatically)\n appDir: z.string().default('app'),\n publicDir: z.string().default('public'),\n\n // Stylesheets\n styles: z.array(z.string()).default([]),\n\n // Favicon\n favicon: z.string().nullable().default(null),\n});\n\nexport type VelixConfig = z.infer<typeof VelixConfigSchema>;\n\n// ============================================================================\n// Default configuration\n// ============================================================================\n\nexport const defaultConfig: VelixConfig = VelixConfigSchema.parse({});\n\n// ============================================================================\n// defineConfig helper\n// ============================================================================\n\n/**\n * Helper function to define configuration with full type support.\n * Use this in your velix.config.ts file.\n *\n * @example\n * ```ts\n * import { defineConfig } from \"velix\";\n *\n * export default defineConfig({\n * app: { name: \"My App\" },\n * server: { port: 3000 },\n * seo: { sitemap: true }\n * });\n * ```\n */\nexport function defineConfig(config: Partial<VelixConfig>): Partial<VelixConfig> {\n return config;\n}\n\n// ============================================================================\n// Config Loader\n// ============================================================================\n\n/**\n * Loads and validates configuration from velix.config.ts\n */\nexport async function loadConfig(projectRoot: string): Promise<VelixConfig> {\n const configPathTs = path.join(projectRoot, 'velix.config.ts');\n const configPathJs = path.join(projectRoot, 'velix.config.js');\n // Support legacy config for migration\n const configPathLegacyTs = path.join(projectRoot, 'flexireact.config.ts');\n const configPathLegacyJs = path.join(projectRoot, 'flexireact.config.js');\n\n let configPath: string | null = null;\n if (fs.existsSync(configPathTs)) configPath = configPathTs;\n else if (fs.existsSync(configPathJs)) configPath = configPathJs;\n else if (fs.existsSync(configPathLegacyTs)) configPath = configPathLegacyTs;\n else if (fs.existsSync(configPathLegacyJs)) configPath = configPathLegacyJs;\n\n let userConfig: Partial<VelixConfig> = {};\n\n if (configPath) {\n try {\n const configUrl = pathToFileURL(configPath).href;\n const module = await import(`${configUrl}?t=${Date.now()}`);\n userConfig = module.default || module;\n } catch (error: any) {\n console.warn(pc.yellow(`⚠ Failed to load config: ${error.message}`));\n }\n }\n\n // Merge and validate with Zod\n const merged = deepMerge(defaultConfig, userConfig as Record<string, any>);\n\n try {\n return VelixConfigSchema.parse(merged);\n } catch (err: unknown) {\n if (err instanceof z.ZodError) {\n console.error(pc.red('✖ Configuration validation failed:'));\n for (const issue of err.issues) {\n console.error(pc.dim(` - ${issue.path.join('.')}: ${issue.message}`));\n }\n process.exit(1);\n }\n throw err;\n }\n}\n\n// ============================================================================\n// Path Resolution\n// ============================================================================\n\n/**\n * Resolves all paths in config relative to project root\n */\nexport function resolvePaths(config: VelixConfig, projectRoot: string): VelixConfig & { resolvedAppDir: string; resolvedPublicDir: string; resolvedOutDir: string } {\n return {\n ...config,\n resolvedAppDir: path.resolve(projectRoot, config.appDir),\n resolvedPublicDir: path.resolve(projectRoot, config.publicDir),\n resolvedOutDir: path.resolve(projectRoot, config.build.outDir),\n };\n}\n\n// ============================================================================\n// Utility: Deep merge\n// ============================================================================\n\nfunction deepMerge(target: Record<string, any>, source: Record<string, any>): Record<string, any> {\n const result = { ...target };\n\n for (const key in source) {\n if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {\n result[key] = deepMerge(target[key] || {}, source[key]);\n } else {\n result[key] = source[key];\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"config.js"}
@@ -0,0 +1,66 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ // server/image-optimizer.ts
5
+ async function handleImageOptimization(req, res, projectRoot) {
6
+ let sharp;
7
+ try {
8
+ sharp = await import('sharp').then((m) => m.default || m);
9
+ } catch (e) {
10
+ }
11
+ try {
12
+ const url = new URL(req.url, `http://${req.headers.host || "localhost"}`);
13
+ const imageUrl = url.searchParams.get("url");
14
+ const widthStr = url.searchParams.get("w");
15
+ const qualityStr = url.searchParams.get("q");
16
+ if (!imageUrl) {
17
+ res.writeHead(400);
18
+ res.end("Missing url parameter");
19
+ return;
20
+ }
21
+ const width = widthStr ? parseInt(widthStr, 10) : void 0;
22
+ const quality = qualityStr ? parseInt(qualityStr, 10) : 75;
23
+ let imageBuffer = null;
24
+ if (imageUrl.startsWith("http")) {
25
+ const response = await fetch(imageUrl);
26
+ if (!response.ok) throw new Error(`Failed to fetch ${imageUrl}`);
27
+ imageBuffer = Buffer.from(await response.arrayBuffer());
28
+ } else {
29
+ const publicDir = path.join(projectRoot, "public");
30
+ const resolvedPath = path.join(publicDir, imageUrl.startsWith("/") ? imageUrl.slice(1) : imageUrl);
31
+ if (!resolvedPath.startsWith(publicDir) || !fs.existsSync(resolvedPath)) {
32
+ res.writeHead(404);
33
+ res.end("Image not found");
34
+ return;
35
+ }
36
+ imageBuffer = fs.readFileSync(resolvedPath);
37
+ }
38
+ if (!sharp) {
39
+ res.writeHead(200, {
40
+ "Content-Type": "image/jpeg",
41
+ "Cache-Control": "public, max-age=31536000, immutable"
42
+ });
43
+ res.end(imageBuffer);
44
+ return;
45
+ }
46
+ let processor = sharp(imageBuffer);
47
+ if (width) {
48
+ processor = processor.resize(width);
49
+ }
50
+ processor = processor.webp({ quality });
51
+ const optimizedBuffer = await processor.toBuffer();
52
+ res.writeHead(200, {
53
+ "Content-Type": "image/webp",
54
+ "Cache-Control": "public, max-age=31536000, immutable"
55
+ });
56
+ res.end(optimizedBuffer);
57
+ } catch (error) {
58
+ console.error("Image optimization error:", error);
59
+ res.writeHead(500);
60
+ res.end("Error processing image");
61
+ }
62
+ }
63
+
64
+ export { handleImageOptimization };
65
+ //# sourceMappingURL=image-optimizer-I6TWWH6W.js.map
66
+ //# sourceMappingURL=image-optimizer-I6TWWH6W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../server/image-optimizer.ts"],"names":[],"mappings":"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","file":"image-optimizer-I6TWWH6W.js","sourcesContent":["import http from 'http';\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nexport async function handleImageOptimization(req: http.IncomingMessage, res: http.ServerResponse, projectRoot: string) {\n let sharp: any;\n try {\n // @ts-ignore: sharp is an optional dependency\n sharp = await import('sharp').then(m => m.default || m);\n } catch (e) {\n // sharp is optional\n }\n try {\n const url = new URL(req.url!, `http://${req.headers.host || 'localhost'}`);\n const imageUrl = url.searchParams.get('url');\n const widthStr = url.searchParams.get('w');\n const qualityStr = url.searchParams.get('q');\n\n if (!imageUrl) {\n res.writeHead(400);\n res.end('Missing url parameter');\n return;\n }\n\n const width = widthStr ? parseInt(widthStr, 10) : undefined;\n const quality = qualityStr ? parseInt(qualityStr, 10) : 75;\n\n // Resolve image path (assuming it's a local public asset if it doesn't start with http)\n let imageBuffer: Buffer | null = null;\n\n if (imageUrl.startsWith('http')) {\n const response = await fetch(imageUrl);\n if (!response.ok) throw new Error(`Failed to fetch ${imageUrl}`);\n imageBuffer = Buffer.from(await response.arrayBuffer());\n } else {\n const publicDir = path.join(projectRoot, 'public');\n // Prevent directory traversal\n const resolvedPath = path.join(publicDir, imageUrl.startsWith('/') ? imageUrl.slice(1) : imageUrl);\n if (!resolvedPath.startsWith(publicDir) || !fs.existsSync(resolvedPath)) {\n res.writeHead(404);\n res.end('Image not found');\n return;\n }\n imageBuffer = fs.readFileSync(resolvedPath);\n }\n\n if (!sharp) {\n // Fallback: just return the original image if sharp is not installed\n res.writeHead(200, {\n 'Content-Type': 'image/jpeg',\n 'Cache-Control': 'public, max-age=31536000, immutable'\n });\n res.end(imageBuffer);\n return;\n }\n\n // Process with sharp\n let processor = sharp(imageBuffer);\n \n if (width) {\n processor = processor.resize(width);\n }\n\n // Default to webp for optimized delivery\n processor = processor.webp({ quality });\n\n const optimizedBuffer = await processor.toBuffer();\n\n res.writeHead(200, {\n 'Content-Type': 'image/webp',\n 'Cache-Control': 'public, max-age=31536000, immutable'\n });\n res.end(optimizedBuffer);\n } catch (error: any) {\n console.error('Image optimization error:', error);\n res.writeHead(500);\n res.end('Error processing image');\n }\n}\n"]}
@@ -79,6 +79,7 @@ interface TailwindPluginOptions {
79
79
  /**
80
80
  * Native Velix Tailwind CSS Plugin
81
81
  * Automatically handles CSS compilation and injection.
82
+ * Supports both Tailwind CSS v3 and v4.
82
83
  */
83
84
  declare function tailwindPlugin(options?: TailwindPluginOptions): VelixPluginDefinition;
84
85
 
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import * as http from 'http';
3
3
  import { IncomingMessage, ServerResponse } from 'http';
4
4
  import React, { ReactNode, ComponentType, ImgHTMLAttributes } from 'react';
5
5
  export { use, useActionState, useOptimistic } from 'react';
6
- export { a as PluginHooks, P as PluginManager, V as VelixServer, c as createServer, d as definePlugin, l as loadPlugins, p as pluginManager, t as tailwindPlugin } from './index-l0dwPa62.js';
6
+ export { a as PluginHooks, P as PluginManager, V as VelixServer, c as createServer, d as definePlugin, l as loadPlugins, p as pluginManager, t as tailwindPlugin } from './index-DYgxL3mE.js';
7
7
  export { Island, LoadStrategy, createIsland, createLazyIsland, generateAdvancedHydrationScript, generateHydrationScript, getRegisteredIslands } from './islands/index.js';
8
8
  export { L as LayoutContext, f as Link, R as RequestContext, d as RouteContext, e as createRequestContext, h as hydrate, r as router, u as useParams, b as usePathname, a as useQuery, c as useRequest, g as useRouter } from './index-C4udNtpZ.js';
9
9
  export { build } from './build/index.js';