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
@@ -0,0 +1,161 @@
import { readFile } from "node:fs/promises";
import { describe, expect, it } from "vitest";
import type {
BrowserDataResult,
IndexedDbRepositoryPort,
} from "../../../src/application/ports/browser-file-storage/index.ts";
import {
createLocalDraftStore,
type LocalDraft,
} from "./fixtures/local-draft-feature.ts";
function success<Value>(value: Value): BrowserDataResult<Value> {
return Object.freeze({ ok: true as const, value });
}
function repositoryFixture(): IndexedDbRepositoryPort<LocalDraft, never> {
let stored: Readonly<{ value: LocalDraft; revision: number }> | null = null;
const repository: IndexedDbRepositoryPort<LocalDraft, never> = {
async open() {
return success(undefined);
},
async read(key) {
if (stored === null || stored.value.draftId !== key) {
return success(null);
}
return success(stored);
},
async query() {
return success(Object.freeze({ items: [], nextCursor: null }));
},
async compareAndSwap(input) {
const currentRevision = stored?.revision ?? null;
if (currentRevision !== input.expectedRevision) {
return Object.freeze({
ok: false as const,
error: Object.freeze({
code: "CONFLICT" as const,
operation: "INDEXEDDB_WRITE" as const,
retryable: false,
recovery: "RECONCILE" as const,
}),
});
}
const revision = (currentRevision ?? 0) + 1;
stored = Object.freeze({ value: input.value, revision });
return success(
Object.freeze({
key: input.key,
revision,
replayed: false,
}),
);
},
async remove(input) {
if (stored === null || stored.revision !== input.expectedRevision) {
return Object.freeze({
ok: false as const,
error: Object.freeze({
code: "CONFLICT" as const,
operation: "INDEXEDDB_WRITE" as const,
retryable: false,
recovery: "RECONCILE" as const,
}),
});
}
stored = null;
return success(
Object.freeze({
key: input.key,
revision: input.expectedRevision + 1,
replayed: false,
}),
);
},
async enforceLifecycleBatch() {
stored = null;
return success(
Object.freeze({
state: "COMPLETE" as const,
scannedRows: 0,
deletedRows: 0,
budgetExhausted: false,
}),
);
},
getStatus() {
return Object.freeze({ kind: "READY" as const, schemaVersion: 1 });
},
subscribeStatus() {
return () => {};
},
close() {},
};
return Object.freeze(repository);
}
describe("IndexedDB local-draft consumer experience", () => {
it("implements save/find/remove through the public application port", async () => {
const store = createLocalDraftStore(repositoryFixture());
const draft = Object.freeze({
draftId: "draft-1",
title: "Architecture notes",
body: "Feature code owns the draft model.",
});
await expect(
store.save({
draft,
expectedRevision: null,
idempotencyKey: "draft-save-0001",
}),
).resolves.toEqual({ ok: true, value: { revision: 1 } });
await expect(store.find("draft-1")).resolves.toEqual({
ok: true,
value: { draft, revision: 1 },
});
await expect(
store.remove({
draftId: "draft-1",
expectedRevision: 1,
idempotencyKey: "draft-remove-0001",
}),
).resolves.toEqual({ ok: true, value: undefined });
await expect(store.find("draft-1")).resolves.toEqual({
ok: true,
value: null,
});
});
it("keeps native IndexedDB and runtime internals out of feature-owned code", async () => {
const source = await readFile(
new URL("./fixtures/local-draft-feature.ts", import.meta.url),
"utf8",
);
const importLines = source
.split("\n")
.filter((line) => line.startsWith("import "));
expect(importLines).toHaveLength(1);
expect(source).toContain(
"src/application/ports/browser-file-storage/index.ts",
);
for (const forbidden of [
"src/adapters/storage/indexeddb",
"globalThis.indexedDB",
"IDBFactory",
"IDBDatabase",
"IDBTransaction",
"IDBObjectStore",
]) {
expect(source).not.toContain(forbidden);
}
});
});