refactor: 프론트 템플릿 리펙토링

This commit is contained in:
donghyeon-ka
2026-09-18 15:16:58 +09:00
parent c10a709f2c
commit 5cc41467ae
80 changed files with 7227 additions and 4672 deletions
+247
View File
@@ -0,0 +1,247 @@
import { PRESIGNED_TRANSFER_PROTOCOL } from "../../src/application/ports/browser-transfer/presigned-transfer.ts";
import type {
PresignedDownloadByteSource,
} from "../../src/application/ports/browser-transfer/presigned-transfer.ts";
import { RESUMABLE_UPLOAD_PROTOCOL } from "../../src/application/ports/browser-transfer/resumable-upload.ts";
import type { BrowserDataObservation } from "../../src/application/ports/browser-file-storage/shared.ts";
import {
createPresignedCapabilityVault,
createSingleUsePresignedReplayGuard,
} from "../../src/adapters/browser-transfer/presigned/presigned-capability-vault.ts";
import { createPresignedCapabilityHttpProvider } from "../../src/adapters/browser-transfer/presigned/presigned-capability-http-provider.ts";
import {
createPresignedTransferExecutor,
type PresignedTransferExecutorOptions,
} from "../../src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts";
import { sha256Hex } from "../../src/adapters/browser-transfer/presigned/incremental-sha256.ts";
export const NOW = 1_000_000;
export const CONTROL_ENDPOINT = "https://api.example/capabilities";
export const DATA_ORIGIN = "https://objects.example";
export const DOWNLOAD_PATH = "/files/resource-1";
export const DOWNLOAD_HREF =
`${DATA_ORIGIN}${DOWNLOAD_PATH}?sig=do-not-log-this`;
export const POLICY_HEADER = "x-policy-version";
export const DIGEST_HEADER = "x-content-sha256";
export const CHECKSUM_HEADER = "x-checksum-sha256";
export const UPLOAD_SESSION_ID = "upload-session-1";
export const REQUEST_BINDING_SHA256 = "c".repeat(64);
export function downloadCapabilityPayload(
bytes: Uint8Array,
overrides: Readonly<Record<string, unknown>> = {},
) {
const digest = sha256Hex(bytes);
return {
protocol: PRESIGNED_TRANSFER_PROTOCOL,
capabilityReceipt: "capability-download-1",
method: "GET",
binding: {
kind: "DOWNLOAD",
resourceId: "resource-1",
},
href: DOWNLOAD_HREF,
origin: DATA_ORIGIN,
path: DOWNLOAD_PATH,
allowedQueryParameters: ["sig"],
requestHeaders: [
{ name: "accept", value: "application/octet-stream" },
],
requiredResponseHeaders: [
{ name: POLICY_HEADER, value: "v1" },
],
digestRequestHeader: null,
digestResponseHeader: DIGEST_HEADER,
receiptResponseHeader: null,
expectedStatus: 200,
expectedResponseByteLength: null,
mediaType: "application/octet-stream",
byteLength: bytes.byteLength,
maxBytes: 64,
expectedSha256: digest,
expiresAtEpochMs: NOW + 30_000,
singleUse: true,
...overrides,
};
}
export type CapabilityPayload = ReturnType<typeof downloadCapabilityPayload>;
export function uploadCapabilityPayload(input: Readonly<{
bytes: Uint8Array;
checksum: string;
}>, overrides: Readonly<Record<string, unknown>> = {}) {
return {
protocol: PRESIGNED_TRANSFER_PROTOCOL,
capabilityReceipt: "capability-upload-1",
method: "PUT",
binding: {
kind: "UPLOAD_PART",
protocol: RESUMABLE_UPLOAD_PROTOCOL,
sessionId: UPLOAD_SESSION_ID,
requestBindingSha256: REQUEST_BINDING_SHA256,
uploadBindingSha256: "b".repeat(64),
partNumber: 1,
offset: 0,
idempotencyKey: "part-attempt-1",
},
href: `${DATA_ORIGIN}/uploads/session-1/part-1?sig=secret`,
origin: DATA_ORIGIN,
path: "/uploads/session-1/part-1",
allowedQueryParameters: ["sig"],
requestHeaders: [
{ name: "content-type", value: "application/octet-stream" },
{ name: CHECKSUM_HEADER, value: input.checksum },
],
requiredResponseHeaders: [
{ name: POLICY_HEADER, value: "v1" },
],
digestRequestHeader: CHECKSUM_HEADER,
digestResponseHeader: null,
receiptResponseHeader: "etag",
expectedStatus: 200,
expectedResponseByteLength: 0,
mediaType: "application/octet-stream",
byteLength: input.bytes.byteLength,
maxBytes: 64,
expectedSha256: input.checksum,
expiresAtEpochMs: NOW + 30_000,
singleUse: true,
...overrides,
};
}
export function jsonResponse(
value: unknown,
url = CONTROL_ENDPOINT,
): Response {
return responseWithUrl(
new Response(JSON.stringify(value), {
status: 200,
headers: { "Content-Type": "application/json" },
}),
url,
);
}
export function downloadResponse(
body: BodyInit | null,
payload: CapabilityPayload,
headers: Record<string, string> = {},
): Response {
return responseWithUrl(
new Response(body, {
status: payload.expectedStatus as number,
headers: {
"Content-Type": String(payload.mediaType),
"Content-Length": String(payload.byteLength),
[DIGEST_HEADER]: String(payload.expectedSha256),
[POLICY_HEADER]: "v1",
...headers,
},
}),
String(payload.href),
);
}
export function responseWithUrl(response: Response, href: string): Response {
Object.defineProperty(response, "url", {
configurable: true,
value: href,
});
return response;
}
export function createHarness(input: Readonly<{
fetcher: typeof fetch;
maxActiveCapabilities?: number;
now?: () => number;
digestBytes?: PresignedTransferExecutorOptions["digestBytes"];
scheduler?: PresignedTransferExecutorOptions["scheduler"];
observer?: Readonly<{
record(observation: BrowserDataObservation): void;
}>;
}>) {
const now = input.now ?? (() => NOW);
const vault = createPresignedCapabilityVault({
maxActiveCapabilities: input.maxActiveCapabilities ?? 16,
now,
});
const replayGuard = createSingleUsePresignedReplayGuard();
const provider = createPresignedCapabilityHttpProvider({
endpoint: CONTROL_ENDPOINT,
vault,
allowedDataOrigins: [DATA_ORIGIN],
allowedDataPathPrefixes: ["/files/", "/uploads/"],
allowedQueryParameters: ["sig"],
allowedRequestHeaders: [
"accept",
"content-type",
CHECKSUM_HEADER,
],
allowedResponseHeaders: [
POLICY_HEADER,
DIGEST_HEADER,
"etag",
],
hardMaxTransferBytes: 64,
hardMaxUploadResponseBytes: 16,
maxCapabilityTtlMs: 60_000,
minimumRemainingLifetimeMs: 1_000,
timeoutMs: 5_000,
fetcher: input.fetcher,
now,
scheduler: input.scheduler,
observer: input.observer,
});
const executor = createPresignedTransferExecutor({
vault,
replayGuard,
hardMaxTransferBytes: 64,
hardMaxChunkBytes: 2,
hardMaxUploadResponseBytes: 16,
minimumRemainingLifetimeMs: 1_000,
timeoutMs: 5_000,
fetcher: input.fetcher,
now,
scheduler: input.scheduler,
digestBytes: input.digestBytes,
observer: input.observer,
});
return { provider, executor, vault };
}
export async function collect(
source: PresignedDownloadByteSource,
signal = new AbortController().signal,
) {
const results = [];
for await (const result of source.stream(signal)) {
results.push(result);
}
return results;
}
/**
* BT-PRE-01. The download lease is lazy, so a response-shape rejection is
* observed on first consumption rather than at open().
*/
export async function firstStreamResult(
opened: Awaited<
ReturnType<
ReturnType<typeof createHarness>["executor"]["downloadSources"]["open"]
>
>,
): Promise<unknown> {
if (!opened.ok) return opened;
try {
for await (const chunk of opened.value.stream(
new AbortController().signal,
)) {
if (!chunk.ok) return chunk;
}
return { ok: true };
} finally {
opened.value.close();
}
}