@nexus_js/assets 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nexus Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # @nexus_js/assets
2
+
3
+ Nexus asset pipeline — image optimization (AVIF/WebP), font injection, lazy loading.
4
+
5
+ ## Documentation
6
+
7
+ All guides, API reference, and examples live on **[nexusjs.dev](https://nexusjs.dev)**.
8
+
9
+ ## Links
10
+
11
+ - **Website:** [https://nexusjs.dev](https://nexusjs.dev)
12
+ - **Repository:** [github.com/bierfor/nexus](https://github.com/bierfor/nexus) (see `packages/assets/`)
13
+ - **Issues:** [github.com/bierfor/nexus/issues](https://github.com/bierfor/nexus/issues)
14
+
15
+ ## License
16
+
17
+ MIT © Nexus contributors
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Nexus Font Optimizer — eliminates CLS from web fonts.
3
+ *
4
+ * Strategies:
5
+ * 1. `inline`: Fetches font CSS at build time, inlines @font-face into <style>.
6
+ * Eliminates extra round-trip. Best for 1-2 fonts.
7
+ * 2. `preload`: Emits <link rel="preload"> for the font files detected.
8
+ * Best for fonts already self-hosted.
9
+ * 3. `swap`: Wraps with `font-display: swap` for graceful loading.
10
+ * 4. `subsets`: Requests only the character ranges actually used (via Unicode-range).
11
+ *
12
+ * Usage in nexus.config.ts:
13
+ * fonts: {
14
+ * google: [{ family: 'Inter', weights: [400, 700] }],
15
+ * local: [{ src: '/fonts/brand.woff2', family: 'Brand' }],
16
+ * strategy: 'inline',
17
+ * }
18
+ */
19
+ export type FontStrategy = 'inline' | 'preload' | 'swap' | 'auto';
20
+ export interface GoogleFont {
21
+ family: string;
22
+ weights?: number[];
23
+ styles?: ('normal' | 'italic')[];
24
+ subsets?: string[];
25
+ display?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
26
+ }
27
+ export interface LocalFont {
28
+ family: string;
29
+ src: string;
30
+ weight?: number | string;
31
+ style?: 'normal' | 'italic';
32
+ display?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';
33
+ /** Unicode range to subset the font */
34
+ unicodeRange?: string;
35
+ }
36
+ export interface FontConfig {
37
+ google?: GoogleFont[];
38
+ local?: LocalFont[];
39
+ strategy?: FontStrategy;
40
+ }
41
+ export interface FontOutput {
42
+ /** CSS to inject into <head> */
43
+ css: string;
44
+ /** <link> tags to inject into <head> */
45
+ links: string;
46
+ /** Preload hints */
47
+ preloads: string;
48
+ }
49
+ /**
50
+ * Generates optimized font HTML/CSS for injection into the document <head>.
51
+ * Called at build time or per-request (cached).
52
+ */
53
+ export declare function optimizeFonts(config: FontConfig): Promise<FontOutput>;
54
+ /**
55
+ * Detects fonts used in the CSS output and returns preload hints.
56
+ * Parses `url(...)` references inside @font-face blocks.
57
+ */
58
+ export declare function extractFontPreloads(css: string): string[];
59
+ //# sourceMappingURL=fonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;CAC/D;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;IAC9D,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,gCAAgC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CA4B3E;AAiFD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAYzD"}
package/dist/fonts.js ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Nexus Font Optimizer — eliminates CLS from web fonts.
3
+ *
4
+ * Strategies:
5
+ * 1. `inline`: Fetches font CSS at build time, inlines @font-face into <style>.
6
+ * Eliminates extra round-trip. Best for 1-2 fonts.
7
+ * 2. `preload`: Emits <link rel="preload"> for the font files detected.
8
+ * Best for fonts already self-hosted.
9
+ * 3. `swap`: Wraps with `font-display: swap` for graceful loading.
10
+ * 4. `subsets`: Requests only the character ranges actually used (via Unicode-range).
11
+ *
12
+ * Usage in nexus.config.ts:
13
+ * fonts: {
14
+ * google: [{ family: 'Inter', weights: [400, 700] }],
15
+ * local: [{ src: '/fonts/brand.woff2', family: 'Brand' }],
16
+ * strategy: 'inline',
17
+ * }
18
+ */
19
+ /**
20
+ * Generates optimized font HTML/CSS for injection into the document <head>.
21
+ * Called at build time or per-request (cached).
22
+ */
23
+ export async function optimizeFonts(config) {
24
+ const strategy = config.strategy ?? 'auto';
25
+ const cssChunks = [];
26
+ const linkTags = [];
27
+ const preloadTags = [];
28
+ // ── Google Fonts ───────────────────────────────────────────────────────────
29
+ if (config.google?.length) {
30
+ const result = await processGoogleFonts(config.google, strategy);
31
+ cssChunks.push(...result.css);
32
+ linkTags.push(...result.links);
33
+ preloadTags.push(...result.preloads);
34
+ }
35
+ // ── Local Fonts ────────────────────────────────────────────────────────────
36
+ if (config.local?.length) {
37
+ const result = processLocalFonts(config.local);
38
+ cssChunks.push(...result.css);
39
+ preloadTags.push(...result.preloads);
40
+ }
41
+ return {
42
+ css: cssChunks.length > 0
43
+ ? `<style id="nexus-fonts">\n${cssChunks.join('\n')}\n</style>`
44
+ : '',
45
+ links: linkTags.join('\n'),
46
+ preloads: preloadTags.join('\n'),
47
+ };
48
+ }
49
+ async function processGoogleFonts(fonts, strategy) {
50
+ const css = [];
51
+ const links = [];
52
+ const preloads = [];
53
+ for (const font of fonts) {
54
+ const weights = font.weights ?? [400];
55
+ const display = font.display ?? 'swap';
56
+ const subsets = font.subsets ?? ['latin'];
57
+ // Build Google Fonts API v2 URL
58
+ const family = font.family.replace(/ /g, '+');
59
+ const axes = weights.length > 1
60
+ ? `wght@${weights.join(';')}`
61
+ : `wght@${weights[0]}`;
62
+ const googleUrl = `https://fonts.googleapis.com/css2?family=${family}:${axes}&display=${display}&subset=${subsets.join(',')}`;
63
+ if (strategy === 'inline') {
64
+ try {
65
+ const fetched = await fetchWithTimeout(googleUrl, {
66
+ headers: { 'user-agent': 'Mozilla/5.0 (compatible; NexusBot/1.0)' },
67
+ });
68
+ if (fetched.ok) {
69
+ const fontCSS = await fetched.text();
70
+ // Replace font-display to ensure it's correct
71
+ css.push(fontCSS.replace(/font-display:\s*\w+/g, `font-display: ${display}`));
72
+ continue;
73
+ }
74
+ }
75
+ catch {
76
+ // Fall through to link strategy
77
+ }
78
+ }
79
+ // Preconnect + stylesheet link (fallback or non-inline strategy)
80
+ links.push(`<link rel="preconnect" href="https://fonts.googleapis.com">`);
81
+ links.push(`<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>`);
82
+ links.push(`<link rel="stylesheet" href="${googleUrl}" media="print" onload="this.media='all'">`);
83
+ // Noscript fallback
84
+ links.push(`<noscript><link rel="stylesheet" href="${googleUrl}"></noscript>`);
85
+ }
86
+ return { css, links, preloads };
87
+ }
88
+ function processLocalFonts(fonts) {
89
+ const css = [];
90
+ const preloads = [];
91
+ for (const font of fonts) {
92
+ const display = font.display ?? 'swap';
93
+ const ext = font.src.split('.').pop() ?? 'woff2';
94
+ const format = ext === 'woff2' ? 'woff2' : ext === 'woff' ? 'woff' : 'truetype';
95
+ css.push(`@font-face {
96
+ font-family: '${font.family}';
97
+ src: url('${font.src}') format('${format}');
98
+ font-weight: ${font.weight ?? 400};
99
+ font-style: ${font.style ?? 'normal'};
100
+ font-display: ${display};${font.unicodeRange ? `\n unicode-range: ${font.unicodeRange};` : ''}
101
+ }`);
102
+ // Preload woff2 (most efficient format)
103
+ if (ext === 'woff2') {
104
+ preloads.push(`<link rel="preload" href="${font.src}" as="font" type="font/woff2" crossorigin>`);
105
+ }
106
+ }
107
+ return { css, preloads };
108
+ }
109
+ /**
110
+ * Detects fonts used in the CSS output and returns preload hints.
111
+ * Parses `url(...)` references inside @font-face blocks.
112
+ */
113
+ export function extractFontPreloads(css) {
114
+ const preloads = [];
115
+ const re = /url\(['"]?([^'")\s]+\.woff2)['"]?\)/g;
116
+ let m;
117
+ while ((m = re.exec(css)) !== null) {
118
+ if (m[1]) {
119
+ preloads.push(`<link rel="preload" href="${m[1]}" as="font" type="font/woff2" crossorigin>`);
120
+ }
121
+ }
122
+ return [...new Set(preloads)];
123
+ }
124
+ async function fetchWithTimeout(url, init) {
125
+ const controller = new AbortController();
126
+ const timeout = setTimeout(() => controller.abort(), 3000);
127
+ try {
128
+ return await fetch(url, { ...init, signal: controller.signal });
129
+ }
130
+ finally {
131
+ clearTimeout(timeout);
132
+ }
133
+ }
134
+ //# sourceMappingURL=fonts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fonts.js","sourceRoot":"","sources":["../src/fonts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Nexus Image — Server-side optimization + client lazy loading.
3
+ *
4
+ * On the server (build/SSR):
5
+ * - Generates <picture> with AVIF + WebP + original fallback sources
6
+ * - Computes intrinsic width/height to prevent CLS
7
+ * - Emits responsive srcset for each configured breakpoint
8
+ * - Blur placeholder hook (data-nx-blur) for perceived performance
9
+ *
10
+ * On the client:
11
+ * - Native `loading="lazy"` + `decoding="async"`
12
+ * - IntersectionObserver for below-the-fold images (optional island)
13
+ *
14
+ * Usage in .nx templates:
15
+ * import { renderImage } from '@nexus_js/assets';
16
+ * renderImage({ src: "/hero.jpg", alt: "Hero", width: 1280, height: 720, priority: true })
17
+ */
18
+ export interface ImageProps {
19
+ src: string;
20
+ alt: string;
21
+ width?: number;
22
+ height?: number;
23
+ /** Square shorthand: sets both width and height */
24
+ size?: number;
25
+ /** Render sizes attribute for responsive images */
26
+ sizes?: string;
27
+ /** Skip lazy loading (above-the-fold images) */
28
+ priority?: boolean;
29
+ /** Round crop (avatars, icons) */
30
+ round?: boolean;
31
+ /** Custom CSS class */
32
+ class?: string;
33
+ /** Quality 1-100 (default: 80) */
34
+ quality?: number;
35
+ /** Output formats in priority order */
36
+ formats?: ImageFormat[];
37
+ /** Placeholder strategy */
38
+ placeholder?: 'blur' | 'empty' | 'none';
39
+ /** Fetch priority hint */
40
+ fetchpriority?: 'high' | 'low' | 'auto';
41
+ }
42
+ export type ImageFormat = 'avif' | 'webp' | 'png' | 'jpg' | 'original';
43
+ export interface OptimizedImageSrc {
44
+ url: string;
45
+ width: number;
46
+ format: ImageFormat;
47
+ }
48
+ /** Options for {@link handleImageRequest} (Node / Vite dev server). */
49
+ export interface ImageHandlerOptions {
50
+ /**
51
+ * Directory used to resolve local `src` paths (e.g. `/photo.jpg` → `<publicDir>/photo.jpg`).
52
+ * When omitted, local files fall back to passthrough behavior.
53
+ */
54
+ publicDir?: string;
55
+ }
56
+ /**
57
+ * Generates optimized `<picture>` HTML for a given image.
58
+ * This runs on the SERVER at build or request time.
59
+ */
60
+ export declare function renderImage(props: ImageProps): string;
61
+ /**
62
+ * Transforms an image URL into an optimized variant URL.
63
+ * In production, this calls the /_nexus/image endpoint which does real conversion.
64
+ * In dev, it passes through with query params for the dev server to handle.
65
+ */
66
+ export declare function imageUrl(src: string, width: number, format: ImageFormat | 'original', quality: number): string;
67
+ /** HTTP handler for the /_nexus/image endpoint */
68
+ export declare function handleImageRequest(request: Request, options?: ImageHandlerOptions): Promise<Response>;
69
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACxC,0BAA0B;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,UAAU,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,uEAAuE;AACvE,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAQD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAiErD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,WAAW,GAAG,UAAU,EAChC,OAAO,EAAE,MAAM,GACd,MAAM,CASR;AAID,kDAAkD;AAClD,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,QAAQ,CAAC,CA8DnB"}
package/dist/image.js ADDED
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Nexus Image — Server-side optimization + client lazy loading.
3
+ *
4
+ * On the server (build/SSR):
5
+ * - Generates <picture> with AVIF + WebP + original fallback sources
6
+ * - Computes intrinsic width/height to prevent CLS
7
+ * - Emits responsive srcset for each configured breakpoint
8
+ * - Blur placeholder hook (data-nx-blur) for perceived performance
9
+ *
10
+ * On the client:
11
+ * - Native `loading="lazy"` + `decoding="async"`
12
+ * - IntersectionObserver for below-the-fold images (optional island)
13
+ *
14
+ * Usage in .nx templates:
15
+ * import { renderImage } from '@nexus_js/assets';
16
+ * renderImage({ src: "/hero.jpg", alt: "Hero", width: 1280, height: 720, priority: true })
17
+ */
18
+ import { readFile } from 'node:fs/promises';
19
+ import { join, resolve, sep } from 'node:path';
20
+ import sharp from 'sharp';
21
+ /** Default responsive breakpoints (px) */
22
+ const DEFAULT_WIDTHS = [320, 640, 960, 1280, 1920];
23
+ /** Default format priority (most efficient first) */
24
+ const DEFAULT_FORMATS = ['avif', 'webp', 'original'];
25
+ /**
26
+ * Generates optimized `<picture>` HTML for a given image.
27
+ * This runs on the SERVER at build or request time.
28
+ */
29
+ export function renderImage(props) {
30
+ const { src, alt, priority = false, round = false, quality = 80, formats = DEFAULT_FORMATS, placeholder = 'blur', fetchpriority, } = props;
31
+ const w = props.size ?? props.width;
32
+ const h = props.size ?? props.height;
33
+ const loading = priority ? 'eager' : 'lazy';
34
+ const decoding = priority ? 'sync' : 'async';
35
+ const fp = fetchpriority ?? (priority ? 'high' : 'auto');
36
+ const widths = w ? getResponsiveWidths(w) : DEFAULT_WIDTHS;
37
+ const sizesAttr = props.sizes ?? defaultSizes(widths);
38
+ const roundStyle = round ? 'border-radius:50%;' : '';
39
+ const aspectStyle = w && h ? `aspect-ratio:${w}/${h};` : '';
40
+ const classAttr = props.class ? ` class="${props.class}"` : '';
41
+ // Build <source> elements for modern formats
42
+ const sources = formats
43
+ .filter((f) => f !== 'original')
44
+ .map((format) => {
45
+ const srcset = widths
46
+ .map((width) => `${imageUrl(src, width, format, quality)} ${width}w`)
47
+ .join(', ');
48
+ return `<source type="image/${format}" srcset="${srcset}" sizes="${sizesAttr}">`;
49
+ })
50
+ .join('\n ');
51
+ // Fallback <img>
52
+ const fallbackSrcset = widths
53
+ .map((width) => `${imageUrl(src, width, 'original', quality)} ${width}w`)
54
+ .join(', ');
55
+ const dimensionAttrs = [
56
+ w ? `width="${w}"` : '',
57
+ h ? `height="${h}"` : '',
58
+ ].filter(Boolean).join(' ');
59
+ const blurAttr = placeholder === 'blur'
60
+ ? ` data-nx-blur style="${aspectStyle}${roundStyle}background:var(--nx-img-blur,#f0f0f0)"`
61
+ : ` style="${aspectStyle}${roundStyle}"`;
62
+ return `<picture${classAttr}>
63
+ ${sources}
64
+ <img
65
+ src="${imageUrl(src, w ?? 800, 'original', quality)}"
66
+ srcset="${fallbackSrcset}"
67
+ sizes="${sizesAttr}"
68
+ alt="${escapeAttr(alt)}"
69
+ ${dimensionAttrs}
70
+ loading="${loading}"
71
+ decoding="${decoding}"
72
+ fetchpriority="${fp}"${blurAttr}
73
+ data-nx-img
74
+ >
75
+ </picture>`;
76
+ }
77
+ /**
78
+ * Transforms an image URL into an optimized variant URL.
79
+ * In production, this calls the /_nexus/image endpoint which does real conversion.
80
+ * In dev, it passes through with query params for the dev server to handle.
81
+ */
82
+ export function imageUrl(src, width, format, quality) {
83
+ if (src.startsWith('http://') || src.startsWith('https://')) {
84
+ // External URLs: proxy through the image optimizer
85
+ return `/_nexus/image?url=${encodeURIComponent(src)}&w=${width}&f=${format}&q=${quality}`;
86
+ }
87
+ if (format === 'original') {
88
+ return `/_nexus/image?src=${encodeURIComponent(src)}&w=${width}&q=${quality}`;
89
+ }
90
+ return `/_nexus/image?src=${encodeURIComponent(src)}&w=${width}&f=${format}&q=${quality}`;
91
+ }
92
+ const REMOTE_MAX_BYTES = 12 * 1024 * 1024;
93
+ /** HTTP handler for the /_nexus/image endpoint */
94
+ export async function handleImageRequest(request, options = {}) {
95
+ const url = new URL(request.url);
96
+ const src = url.searchParams.get('src') ?? '';
97
+ const remoteUrl = url.searchParams.get('url') ?? '';
98
+ const width = Math.min(Math.max(parseInt(url.searchParams.get('w') ?? '800', 10) || 800, 1), 8192);
99
+ const hasExplicitFormat = url.searchParams.has('f');
100
+ const formatParam = (url.searchParams.get('f') ?? 'original');
101
+ const quality = Math.min(Math.max(parseInt(url.searchParams.get('q') ?? '80', 10) || 80, 1), 100);
102
+ // Only honor `f=` when present. Omitted `f` means "original" raster (JPEG/PNG), not Accept-based AVIF/WebP,
103
+ // so <picture><source type=avif> and <img> fallback stay distinct.
104
+ const outputFormat = hasExplicitFormat ? formatParam : 'original';
105
+ const cacheHeaders = {
106
+ 'cache-control': 'public, max-age=31536000, immutable',
107
+ };
108
+ try {
109
+ let input;
110
+ let sourceLabel;
111
+ if (remoteUrl) {
112
+ if (!remoteUrl.startsWith('https://') && !remoteUrl.startsWith('http://')) {
113
+ return new Response('Invalid URL', { status: 400 });
114
+ }
115
+ const fetched = await fetchRemoteImage(remoteUrl);
116
+ if (!fetched) {
117
+ return new Response('Failed to fetch image', { status: 502 });
118
+ }
119
+ input = fetched;
120
+ sourceLabel = remoteUrl;
121
+ }
122
+ else if (src) {
123
+ if (!options.publicDir) {
124
+ return new Response('Local src requires publicDir (set in Nexus server / Vite plugin)', { status: 400 });
125
+ }
126
+ const path = safeResolvePublicPath(options.publicDir, src);
127
+ if (!path) {
128
+ return new Response('Invalid path', { status: 400 });
129
+ }
130
+ try {
131
+ input = await readFile(path);
132
+ }
133
+ catch {
134
+ return new Response('Not found', { status: 404 });
135
+ }
136
+ sourceLabel = src;
137
+ }
138
+ else {
139
+ return new Response('Missing src or url', { status: 400 });
140
+ }
141
+ const { body, contentType } = await encodeWithSharp(input, width, outputFormat, quality);
142
+ return new Response(new Uint8Array(body), {
143
+ status: 200,
144
+ headers: {
145
+ ...cacheHeaders,
146
+ 'content-type': contentType,
147
+ 'x-nexus-image-source': encodeURIComponent(sourceLabel.slice(0, 200)),
148
+ },
149
+ });
150
+ }
151
+ catch (err) {
152
+ const message = err instanceof Error ? err.message : String(err);
153
+ return new Response(`Image optimization failed: ${message}`, { status: 500 });
154
+ }
155
+ }
156
+ function safeResolvePublicPath(publicDir, src) {
157
+ const normalized = src.replace(/^\/+/, '');
158
+ if (normalized.includes('..'))
159
+ return null;
160
+ const full = resolve(join(publicDir, normalized));
161
+ const root = resolve(publicDir);
162
+ if (full !== root && !full.startsWith(root + sep))
163
+ return null;
164
+ return full;
165
+ }
166
+ async function fetchRemoteImage(remoteUrl) {
167
+ const ac = new AbortController();
168
+ const t = setTimeout(() => ac.abort(), 15_000);
169
+ try {
170
+ const res = await fetch(remoteUrl, {
171
+ signal: ac.signal,
172
+ headers: { 'user-agent': 'NexusImage/1.0' },
173
+ redirect: 'follow',
174
+ });
175
+ if (!res.ok)
176
+ return null;
177
+ const len = res.headers.get('content-length');
178
+ if (len && Number(len) > REMOTE_MAX_BYTES)
179
+ return null;
180
+ const buf = Buffer.from(await res.arrayBuffer());
181
+ if (buf.length > REMOTE_MAX_BYTES)
182
+ return null;
183
+ return buf;
184
+ }
185
+ catch {
186
+ return null;
187
+ }
188
+ finally {
189
+ clearTimeout(t);
190
+ }
191
+ }
192
+ async function encodeWithSharp(input, width, outputFormat, quality) {
193
+ let pipeline = sharp(input).rotate().resize({
194
+ width,
195
+ withoutEnlargement: true,
196
+ fit: 'inside',
197
+ });
198
+ const meta = await sharp(input).metadata();
199
+ if (outputFormat === 'avif') {
200
+ const buf = await pipeline.avif({ quality }).toBuffer();
201
+ return { body: buf, contentType: 'image/avif' };
202
+ }
203
+ if (outputFormat === 'webp') {
204
+ const buf = await pipeline.webp({ quality }).toBuffer();
205
+ return { body: buf, contentType: 'image/webp' };
206
+ }
207
+ if (outputFormat === 'png') {
208
+ const buf = await pipeline.png({ compressionLevel: 9 }).toBuffer();
209
+ return { body: buf, contentType: 'image/png' };
210
+ }
211
+ if (outputFormat === 'jpg') {
212
+ const buf = await pipeline.jpeg({ quality, mozjpeg: true }).toBuffer();
213
+ return { body: buf, contentType: 'image/jpeg' };
214
+ }
215
+ // original: resize only, preserve alpha when present
216
+ if (meta.hasAlpha || meta.format === 'png') {
217
+ const buf = await pipeline.png({ compressionLevel: 9 }).toBuffer();
218
+ return { body: buf, contentType: 'image/png' };
219
+ }
220
+ const buf = await pipeline.jpeg({ quality, mozjpeg: true }).toBuffer();
221
+ return { body: buf, contentType: 'image/jpeg' };
222
+ }
223
+ function getResponsiveWidths(maxWidth) {
224
+ return DEFAULT_WIDTHS.filter((w) => w <= maxWidth * 1.5);
225
+ }
226
+ function defaultSizes(widths) {
227
+ const max = widths[widths.length - 1];
228
+ return max ? `(max-width: ${max}px) 100vw, ${max}px` : '100vw';
229
+ }
230
+ function escapeAttr(str) {
231
+ return str.replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
232
+ }
233
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../src/image.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { renderImage, imageUrl, handleImageRequest } from './image.js';
2
+ export type { ImageProps, ImageFormat, OptimizedImageSrc, ImageHandlerOptions } from './image.js';
3
+ export { optimizeFonts, extractFontPreloads } from './fonts.js';
4
+ export type { FontConfig, FontStrategy, GoogleFont, LocalFont, FontOutput } from './fonts.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACvE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAClG,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAChE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { renderImage, imageUrl, handleImageRequest } from './image.js';
2
+ export { optimizeFonts, extractFontPreloads } from './fonts.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEvE,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@nexus_js/assets",
3
+ "version": "0.6.0",
4
+ "description": "Nexus asset pipeline — image optimization (AVIF/WebP), font injection, lazy loading",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ },
13
+ "./image": {
14
+ "import": "./dist/image.js",
15
+ "types": "./dist/image.d.ts"
16
+ },
17
+ "./fonts": {
18
+ "import": "./dist/fonts.js",
19
+ "types": "./dist/fonts.d.ts"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "sharp": "^0.33.5"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.0.0",
27
+ "typescript": "^5.5.0"
28
+ },
29
+ "license": "MIT",
30
+ "homepage": "https://nexusjs.dev",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/bierfor/nexus.git",
34
+ "directory": "packages/assets"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/bierfor/nexus/issues"
38
+ },
39
+ "keywords": [
40
+ "nexus",
41
+ "framework",
42
+ "full-stack",
43
+ "svelte",
44
+ "islands",
45
+ "ssr",
46
+ "vite",
47
+ "server-actions"
48
+ ],
49
+ "files": [
50
+ "dist",
51
+ "README.md"
52
+ ],
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "registry": "https://registry.npmjs.org/"
56
+ },
57
+ "scripts": {
58
+ "build": "tsc -p tsconfig.json",
59
+ "dev": "tsc -p tsconfig.json --watch",
60
+ "clean": "rm -rf dist"
61
+ }
62
+ }