refactor: 프론트 템플릿 리펙토링
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
import { vi } from "vitest";
|
||||
|
||||
import type {
|
||||
ImageProbeScheduler,
|
||||
} from "../../src/adapters/browser-transfer/image-cdn/browser-image-probe.ts";
|
||||
import type {
|
||||
ImageCapabilityVerificationScheduler,
|
||||
} from "../../src/adapters/browser-transfer/image-cdn/image-cdn-runtime.ts";
|
||||
|
||||
export function responseAt(
|
||||
url: string,
|
||||
body: Uint8Array,
|
||||
init: ResponseInit,
|
||||
): Response {
|
||||
const responseBytes = new Uint8Array(body.byteLength);
|
||||
responseBytes.set(body);
|
||||
const response = new Response(responseBytes.buffer, init);
|
||||
Object.defineProperty(response, "url", {
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
value: url,
|
||||
});
|
||||
return response;
|
||||
}
|
||||
|
||||
export function publicImageHeaders(
|
||||
mediaType: string,
|
||||
contentLength?: number,
|
||||
): Headers {
|
||||
const headers = new Headers({
|
||||
"cache-control":
|
||||
"public, max-age=31536000, s-maxage=31536000, immutable",
|
||||
"content-type": mediaType,
|
||||
vary: "Accept-Encoding",
|
||||
});
|
||||
if (contentLength !== undefined) {
|
||||
headers.set("content-length", String(contentLength));
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
export function pngBytes(
|
||||
width: number,
|
||||
height: number,
|
||||
animated = false,
|
||||
): Uint8Array {
|
||||
const header = new Uint8Array(13);
|
||||
const headerView = new DataView(header.buffer);
|
||||
headerView.setUint32(0, width);
|
||||
headerView.setUint32(4, height);
|
||||
header[8] = 8;
|
||||
header[9] = 6;
|
||||
return concatenateBytes([
|
||||
Uint8Array.from([
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
||||
]),
|
||||
pngChunk("IHDR", header),
|
||||
...(animated
|
||||
? [pngChunk("acTL", new Uint8Array(8))]
|
||||
: []),
|
||||
pngChunk("IDAT", new Uint8Array()),
|
||||
pngChunk("IEND", new Uint8Array()),
|
||||
]);
|
||||
}
|
||||
|
||||
function pngChunk(type: string, payload: Uint8Array): Uint8Array {
|
||||
const chunk = new Uint8Array(12 + payload.byteLength);
|
||||
const view = new DataView(chunk.buffer);
|
||||
view.setUint32(0, payload.byteLength);
|
||||
writeAscii(chunk, 4, type);
|
||||
chunk.set(payload, 8);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
export function jpegBytes(width: number, height: number): Uint8Array {
|
||||
return Uint8Array.from([
|
||||
0xff,
|
||||
0xd8,
|
||||
0xff,
|
||||
0xc0,
|
||||
0x00,
|
||||
0x11,
|
||||
0x08,
|
||||
(height >>> 8) & 0xff,
|
||||
height & 0xff,
|
||||
(width >>> 8) & 0xff,
|
||||
width & 0xff,
|
||||
0x03,
|
||||
0x01,
|
||||
0x11,
|
||||
0x00,
|
||||
0x02,
|
||||
0x11,
|
||||
0x00,
|
||||
0x03,
|
||||
0x11,
|
||||
0x00,
|
||||
0xff,
|
||||
0xda,
|
||||
]);
|
||||
}
|
||||
|
||||
export function webpBytes(
|
||||
width: number,
|
||||
height: number,
|
||||
animated = false,
|
||||
): Uint8Array {
|
||||
const chunkType = animated ? "VP8X" : "VP8 ";
|
||||
const payload = new Uint8Array(10);
|
||||
if (animated) {
|
||||
payload[0] = 0x02;
|
||||
writeUint24LittleEndian(payload, 4, width - 1);
|
||||
writeUint24LittleEndian(payload, 7, height - 1);
|
||||
} else {
|
||||
payload.set([0x9d, 0x01, 0x2a], 3);
|
||||
const view = new DataView(payload.buffer);
|
||||
view.setUint16(6, width, true);
|
||||
view.setUint16(8, height, true);
|
||||
}
|
||||
const chunk = concatenateBytes([
|
||||
asciiBytes(chunkType),
|
||||
littleEndianUint32(payload.byteLength),
|
||||
payload,
|
||||
]);
|
||||
return concatenateBytes([
|
||||
asciiBytes("RIFF"),
|
||||
littleEndianUint32(4 + chunk.byteLength),
|
||||
asciiBytes("WEBP"),
|
||||
chunk,
|
||||
]);
|
||||
}
|
||||
|
||||
export function avifBytes(
|
||||
width: number,
|
||||
height: number,
|
||||
brand = "avif",
|
||||
): Uint8Array {
|
||||
const fileType = isoBox(
|
||||
"ftyp",
|
||||
concatenateBytes([
|
||||
asciiBytes(brand),
|
||||
new Uint8Array(4),
|
||||
asciiBytes(brand),
|
||||
]),
|
||||
);
|
||||
const spatialExtent = new Uint8Array(12);
|
||||
const extentView = new DataView(spatialExtent.buffer);
|
||||
extentView.setUint32(4, width);
|
||||
extentView.setUint32(8, height);
|
||||
const primaryItem = new Uint8Array(6);
|
||||
new DataView(primaryItem.buffer).setUint16(4, 1);
|
||||
const itemInfoEntry = new Uint8Array(13);
|
||||
itemInfoEntry[0] = 2;
|
||||
const itemInfoView = new DataView(itemInfoEntry.buffer);
|
||||
itemInfoView.setUint16(4, 1);
|
||||
writeAscii(itemInfoEntry, 8, "av01");
|
||||
const itemInfo = new Uint8Array(6);
|
||||
new DataView(itemInfo.buffer).setUint16(4, 1);
|
||||
const propertyAssociation = new Uint8Array(12);
|
||||
const associationView = new DataView(
|
||||
propertyAssociation.buffer,
|
||||
);
|
||||
associationView.setUint32(4, 1);
|
||||
associationView.setUint16(8, 1);
|
||||
propertyAssociation[10] = 1;
|
||||
propertyAssociation[11] = 0x81;
|
||||
const properties = isoBox(
|
||||
"iprp",
|
||||
concatenateBytes([
|
||||
isoBox("ipco", isoBox("ispe", spatialExtent)),
|
||||
isoBox("ipma", propertyAssociation),
|
||||
]),
|
||||
);
|
||||
const metadata = isoBox(
|
||||
"meta",
|
||||
concatenateBytes([
|
||||
new Uint8Array(4),
|
||||
isoBox("pitm", primaryItem),
|
||||
isoBox(
|
||||
"iinf",
|
||||
concatenateBytes([
|
||||
itemInfo,
|
||||
isoBox("infe", itemInfoEntry),
|
||||
]),
|
||||
),
|
||||
properties,
|
||||
]),
|
||||
);
|
||||
return concatenateBytes([
|
||||
fileType,
|
||||
metadata,
|
||||
isoBox("mdat", Uint8Array.of(0)),
|
||||
]);
|
||||
}
|
||||
|
||||
export function isoBox(type: string, payload: Uint8Array): Uint8Array {
|
||||
const box = new Uint8Array(8 + payload.byteLength);
|
||||
const view = new DataView(box.buffer);
|
||||
view.setUint32(0, box.byteLength);
|
||||
writeAscii(box, 4, type);
|
||||
box.set(payload, 8);
|
||||
return box;
|
||||
}
|
||||
|
||||
export function littleEndianUint32(value: number): Uint8Array {
|
||||
const bytes = new Uint8Array(4);
|
||||
new DataView(bytes.buffer).setUint32(0, value, true);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
export function writeUint24LittleEndian(
|
||||
bytes: Uint8Array,
|
||||
offset: number,
|
||||
value: number,
|
||||
): void {
|
||||
bytes[offset] = value & 0xff;
|
||||
bytes[offset + 1] = (value >>> 8) & 0xff;
|
||||
bytes[offset + 2] = (value >>> 16) & 0xff;
|
||||
}
|
||||
|
||||
export function asciiBytes(value: string): Uint8Array {
|
||||
return Uint8Array.from(
|
||||
[...value].map((character) => character.charCodeAt(0)),
|
||||
);
|
||||
}
|
||||
|
||||
export function writeAscii(
|
||||
target: Uint8Array,
|
||||
offset: number,
|
||||
value: string,
|
||||
): void {
|
||||
target.set(asciiBytes(value), offset);
|
||||
}
|
||||
|
||||
export function concatenateBytes(
|
||||
chunks: readonly Uint8Array[],
|
||||
): Uint8Array {
|
||||
const combined = new Uint8Array(
|
||||
chunks.reduce((total, chunk) => total + chunk.byteLength, 0),
|
||||
);
|
||||
let offset = 0;
|
||||
for (const chunk of chunks) {
|
||||
combined.set(chunk, offset);
|
||||
offset += chunk.byteLength;
|
||||
}
|
||||
return combined;
|
||||
}
|
||||
|
||||
export function manualImageProbeScheduler(): Readonly<{
|
||||
scheduler: ImageProbeScheduler;
|
||||
fire(): void;
|
||||
}> {
|
||||
let callback: (() => void) | undefined;
|
||||
return {
|
||||
scheduler: {
|
||||
setTimeout(nextCallback) {
|
||||
callback = nextCallback;
|
||||
return 1;
|
||||
},
|
||||
clearTimeout: vi.fn(),
|
||||
},
|
||||
fire() {
|
||||
if (!callback) {
|
||||
throw new TypeError("No image probe timeout is scheduled.");
|
||||
}
|
||||
callback();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function manualCapabilityVerificationScheduler(): Readonly<{
|
||||
scheduler: ImageCapabilityVerificationScheduler;
|
||||
delays: readonly number[];
|
||||
clearTimeout: ReturnType<typeof vi.fn>;
|
||||
fire(): void;
|
||||
}> {
|
||||
let callback: (() => void) | undefined;
|
||||
const delays: number[] = [];
|
||||
const clearTimeout = vi.fn();
|
||||
return {
|
||||
scheduler: {
|
||||
setTimeout(nextCallback, milliseconds) {
|
||||
callback = nextCallback;
|
||||
delays.push(milliseconds);
|
||||
return 1;
|
||||
},
|
||||
clearTimeout,
|
||||
},
|
||||
delays,
|
||||
clearTimeout,
|
||||
fire() {
|
||||
if (!callback) {
|
||||
throw new TypeError(
|
||||
"No capability verification timeout is scheduled.",
|
||||
);
|
||||
}
|
||||
callback();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function base64Url(bytes: Uint8Array): string {
|
||||
let binary = "";
|
||||
for (const byte of bytes) binary += String.fromCharCode(byte);
|
||||
return btoa(binary)
|
||||
.replace(/\+/gu, "-")
|
||||
.replace(/\//gu, "_")
|
||||
.replace(/=+$/gu, "");
|
||||
}
|
||||
Reference in New Issue
Block a user