서버, 클라이언트, 서비스 워커 공용 코드를 lib/shared로 분리하고 브라우저 전용 모듈을 lib/client로 재구성

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
staticandClaude Opus 5.5 committed 2026-10-12 00:04:56 +09:00
1 parent 6a6aa1765b
commit aee87fd9b6
82 files changed
+326 -221

No files matched your search

+19
View File
@@ -39,6 +39,25 @@ describe("createEncContentStream", () => {
expect(await read(encContentStream)).toBe("2345");
});
it("serves suffix ranges", async () => {
const path = await createFile("0123456789");
const { encContentStream, range } = await createEncContentStream(path, undefined, {
suffixLength: 3,
});
expect(range).toEqual({ start: 7, end: 9, total: 10 });
expect(await read(encContentStream)).toBe("789");
});
it("rejects unsatisfiable ranges", async () => {
const path = await createFile("0123456789");
await expect(createEncContentStream(path, undefined, { start: 10 })).rejects.toMatchObject({
code: "RANGE_NOT_SATISFIABLE",
});
});
it("prepends the legacy IV to the content", async () => {
const path = await createFile("6789");
+1 -1
View File
@@ -7,7 +7,7 @@ import {
encryptChunk,
encryptString,
generateDataKey,
} from "#lib/modules/crypto/index.js";
} from "#lib/shared/crypto/index.js";
describe("base64", () => {
it("round-trips large buffers", () => {
+41 -8
View File
@@ -1,7 +1,13 @@
import { describe, expect, it } from "vitest";
import { CHUNK_SIZE, ENCRYPTED_CHUNK_SIZE, ENCRYPTION_OVERHEAD } from "#lib/constants/index.js";
import { getContentRangeHeader, parseRangeHeader } from "#lib/modules/http.js";
import { getDecryptedSize, getEncryptedRange } from "../../src/service-worker/modules/crypto";
import {
CHUNK_SIZE,
ENCRYPTED_CHUNK_SIZE,
ENCRYPTION_OVERHEAD,
getDecryptedSize,
getEncryptedRange,
getEncryptedSize,
} from "#lib/shared/format/index.js";
import { getContentRangeHeader, parseRangeHeader, resolveRange } from "#lib/shared/http.js";
describe("parseRangeHeader", () => {
it("parses byte ranges", () => {
@@ -9,6 +15,33 @@ describe("parseRangeHeader", () => {
expect(parseRangeHeader("bytes=0-")).toEqual({ start: 0, end: undefined });
expect(parseRangeHeader("bytes=10-20")).toEqual({ start: 10, end: 20 });
expect(parseRangeHeader("bytes=10-20, 30-40")).toEqual({ start: 10, end: 20 });
expect(parseRangeHeader("bytes=-500")).toEqual({ suffixLength: 500 });
});
it("ignores invalid ranges", () => {
expect(parseRangeHeader("bytes=-")).toBeUndefined();
expect(parseRangeHeader("items=0-1")).toBeUndefined();
});
});
describe("resolveRange", () => {
it("resolves the whole content without a range", () => {
expect(resolveRange(undefined, 100)).toEqual({ start: 0, end: 99, total: 100 });
});
it("clamps the end to the content size", () => {
expect(resolveRange({ start: 10, end: 1000 }, 100)).toEqual({ start: 10, end: 99, total: 100 });
expect(resolveRange({ start: 10 }, 100)).toEqual({ start: 10, end: 99, total: 100 });
});
it("resolves suffix ranges", () => {
expect(resolveRange({ suffixLength: 10 }, 100)).toEqual({ start: 90, end: 99, total: 100 });
expect(resolveRange({ suffixLength: 1000 }, 100)).toEqual({ start: 0, end: 99, total: 100 });
});
it("rejects unsatisfiable ranges", () => {
expect(resolveRange({ start: 100 }, 100)).toBeNull();
expect(resolveRange({ start: 20, end: 10 }, 100)).toBeNull();
});
it("formats Content-Range", () => {
@@ -19,13 +52,13 @@ describe("parseRangeHeader", () => {
});
describe("encrypted chunk layout", () => {
const encryptedSizeOf = (size: number) =>
size + Math.ceil(size / CHUNK_SIZE) * ENCRYPTION_OVERHEAD;
it.each([1, CHUNK_SIZE - 1, CHUNK_SIZE, CHUNK_SIZE + 1, 3 * CHUNK_SIZE + 12345])(
"computes the decrypted size of %i bytes",
(size) => {
expect(getDecryptedSize(encryptedSizeOf(size), false)).toBe(size);
expect(getEncryptedSize(size)).toBe(
size + Math.ceil(size / CHUNK_SIZE) * ENCRYPTION_OVERHEAD,
);
expect(getDecryptedSize(getEncryptedSize(size), false)).toBe(size);
},
);
@@ -35,7 +68,7 @@ describe("encrypted chunk layout", () => {
it("maps a plaintext range to whole encrypted chunks", () => {
const totalSize = 3 * CHUNK_SIZE + 100;
const totalEncryptedSize = encryptedSizeOf(totalSize);
const totalEncryptedSize = getEncryptedSize(totalSize);
expect(getEncryptedRange(0, 10, totalEncryptedSize, false)).toEqual({
firstChunkIndex: 0,