release: WRNexusJS 0.7.0

This commit is contained in:
2026-08-01 10:04:42 +05:30
parent c54144f2e4
commit 87507edf59
207 changed files with 12607 additions and 679 deletions
+16
View File
@@ -0,0 +1,16 @@
# @wrnexus/image
Responsive image attribute generation with secure remote-host policies and audits for dimensions, LCP loading, source oversizing, transfer size, and modern formats.
```ts
import { createResponsiveImage } from "@wrnexus/image";
const attrs = createResponsiveImage({
src: "/hero.jpg",
alt: "Hero",
width: 1600,
height: 900,
widths: [480, 960, 1600],
sizes: "100vw",
format: "avif",
});
```
+13
View File
@@ -0,0 +1,13 @@
{
"name": "@wrnexus/image",
"version": "0.7.0",
"type": "module",
"description": "Responsive image planning, secure remote image policies, and performance auditing for WRNexusJS.",
"main": "src/index.ts",
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"@wrnexus/security": "workspace:*"
}
}
+188
View File
@@ -0,0 +1,188 @@
import { validateUrl } from "@wrnexus/security";
export type ImageFormat = "avif" | "webp" | "jpeg" | "png" | "original";
export interface ImageLoaderInput {
src: string;
width: number;
quality?: number;
format?: ImageFormat;
}
export type ImageLoader = (input: ImageLoaderInput) => string;
export interface ImagePolicy {
remoteHosts?: string[];
allowedProtocols?: string[];
maxWidth?: number;
maxQuality?: number;
}
export interface ResponsiveImageOptions extends ImagePolicy {
src: string;
alt: string;
width: number;
height: number;
widths?: number[];
sizes?: string;
quality?: number;
format?: ImageFormat;
loading?: "eager" | "lazy";
fetchPriority?: "high" | "low" | "auto";
decoding?: "async" | "sync" | "auto";
loader?: ImageLoader;
class?: string;
}
export interface ResponsiveImageAttributes {
src: string;
srcset?: string;
sizes?: string;
alt: string;
width: string;
height: string;
loading: "eager" | "lazy";
decoding: "async" | "sync" | "auto";
fetchpriority?: "high" | "low" | "auto";
class?: string;
}
export interface ImageAuditInput {
src: string;
width?: number;
height?: number;
renderedWidth?: number;
bytes?: number;
loading?: string;
fetchPriority?: string;
isLcp?: boolean;
}
export interface ImageAuditIssue {
code: string;
severity: "error" | "warning" | "info";
message: string;
}
export const defaultImageLoader: ImageLoader = ({ src, width, quality, format }) => {
const separator = src.includes("?") ? "&" : "?";
const params = new URLSearchParams({ w: String(width) });
if (quality !== undefined) params.set("q", String(quality));
if (format && format !== "original") params.set("format", format);
return `${src}${separator}${params.toString()}`;
};
function validateSource(src: string, policy: ImagePolicy): void {
if (/^(?:https?:)?\/\//i.test(src)) {
validateUrl(src.startsWith("//") ? `https:${src}` : src, {
allowRelative: false,
allowedProtocols: policy.allowedProtocols ?? ["https:"],
allowedHosts: policy.remoteHosts,
allowCredentials: false,
});
} else {
validateUrl(src, {
base: "https://wrnexus.invalid",
allowRelative: true,
allowedProtocols: ["https:"],
allowCredentials: false,
});
}
}
export function createResponsiveImage(options: ResponsiveImageOptions): ResponsiveImageAttributes {
validateSource(options.src, options);
if (!Number.isFinite(options.width) || options.width <= 0) {
throw new RangeError("Image width must be a positive number.");
}
if (!Number.isFinite(options.height) || options.height <= 0) {
throw new RangeError("Image height must be a positive number.");
}
const maxWidth = options.maxWidth ?? 8_192;
const maxQuality = options.maxQuality ?? 100;
const quality = Math.min(maxQuality, Math.max(1, Math.round(options.quality ?? 80)));
const widths = [...new Set(options.widths ?? [options.width])]
.map((width) => Math.round(width))
.filter((width) => width > 0 && width <= maxWidth)
.sort((a, b) => a - b);
if (!widths.includes(Math.round(options.width)) && options.width <= maxWidth) {
widths.push(Math.round(options.width));
widths.sort((a, b) => a - b);
}
const loader = options.loader ?? defaultImageLoader;
const src = loader({
src: options.src,
width: Math.min(Math.round(options.width), maxWidth),
quality,
format: options.format,
});
const srcset =
widths.length > 1
? widths
.map(
(width) =>
`${loader({ src: options.src, width, quality, format: options.format })} ${width}w`,
)
.join(", ")
: undefined;
return {
src,
...(srcset ? { srcset } : {}),
...(options.sizes ? { sizes: options.sizes } : {}),
alt: options.alt,
width: String(Math.round(options.width)),
height: String(Math.round(options.height)),
loading: options.loading ?? (options.fetchPriority === "high" ? "eager" : "lazy"),
decoding: options.decoding ?? "async",
...(options.fetchPriority ? { fetchpriority: options.fetchPriority } : {}),
...(options.class ? { class: options.class } : {}),
};
}
export function auditImage(input: ImageAuditInput): ImageAuditIssue[] {
const issues: ImageAuditIssue[] = [];
if (!input.width || !input.height) {
issues.push({
code: "WRN-IMAGE-DIMENSIONS",
severity: "error",
message: "Images must declare width and height to prevent layout shifts.",
});
}
if (input.isLcp && input.loading === "lazy") {
issues.push({
code: "WRN-IMAGE-LCP-LAZY",
severity: "error",
message: "The LCP image must not be lazy-loaded.",
});
}
if (input.isLcp && input.fetchPriority !== "high") {
issues.push({
code: "WRN-IMAGE-LCP-PRIORITY",
severity: "warning",
message: "Consider fetchpriority=high for the LCP image.",
});
}
if (input.width && input.renderedWidth && input.width > input.renderedWidth * 2.5) {
issues.push({
code: "WRN-IMAGE-OVERSIZED",
severity: "warning",
message: "The source image is substantially wider than its rendered size.",
});
}
if ((input.bytes ?? 0) > 1_000_000) {
issues.push({
code: "WRN-IMAGE-BYTES",
severity: (input.bytes ?? 0) > 3_000_000 ? "error" : "warning",
message: `Image transfer size is ${Math.round((input.bytes ?? 0) / 1024)} KiB.`,
});
}
if (/\.(?:png|jpe?g)(?:\?|$)/i.test(input.src)) {
issues.push({
code: "WRN-IMAGE-MODERN-FORMAT",
severity: "info",
message: "Consider serving AVIF or WebP with a compatible fallback.",
});
}
return issues;
}
+38
View File
@@ -0,0 +1,38 @@
import { describe, expect, test } from "bun:test";
import { auditImage, createResponsiveImage } from "../src/index.ts";
describe("@wrnexus/image", () => {
test("builds deterministic responsive image attributes", () => {
const image = createResponsiveImage({
src: "/hero.jpg",
alt: "Hero",
width: 1200,
height: 600,
widths: [400, 800, 1200, 800],
sizes: "100vw",
format: "webp",
fetchPriority: "high",
});
expect(image.srcset).toContain("400w");
expect(image.srcset).toContain("1200w");
expect(image.loading).toBe("eager");
expect(image.width).toBe("1200");
});
test("reports layout shift and LCP mistakes", () => {
const issues = auditImage({ src: "hero.jpg", isLcp: true, loading: "lazy" });
expect(issues.map((issue) => issue.code)).toContain("WRN-IMAGE-DIMENSIONS");
expect(issues.map((issue) => issue.code)).toContain("WRN-IMAGE-LCP-LAZY");
});
test("rejects insecure remote images unless explicitly allowed", () => {
expect(() =>
createResponsiveImage({
src: "http://images.example.com/hero.jpg",
alt: "Hero",
width: 800,
height: 400,
remoteHosts: ["images.example.com"],
}),
).toThrow();
});
});