feat: establish TypeScript-aware frontend tooling

This commit is contained in:
donghyeon-ka
2026-07-26 13:41:23 +09:00
parent 1a1747c737
commit 0fed35586a
41 changed files with 1086 additions and 125 deletions
+2 -1
View File
@@ -36,6 +36,7 @@ describe("external AuthSessionPort adapter", () => {
beginSignIn: async () => {},
signOut: async () => {},
attachCredential: async (request) => request,
// @ts-expect-error Deliberately violates the external-owner contract.
recoverSession: async () => "unexpected",
notifyUnauthenticated: vi.fn(),
});
@@ -57,7 +58,7 @@ describe("external AuthSessionPort adapter", () => {
});
expect(adapter.getState()).toBe("unauthenticated");
await adapter.beginSignIn("/");
await adapter.beginSignIn();
expect(adapter.getState()).toBe("authenticated");
await adapter.signOut();
expect(adapter.getState()).toBe("unauthenticated");
+6 -5
View File
@@ -3,15 +3,16 @@ import { describe, expect, it } from "vitest";
import { decideChunkRecovery } from "../../src/application/use-cases/decide-chunk-recovery.js";
function memoryStorage() {
/** @type {unknown} */
let value;
return {
read: () => ({ ok: true, value }),
return /** @type {import("../../src/application/ports/storage-port.js").StoragePort} */ ({
read: () => ({ ok: /** @type {const} */ (true), value }),
write: (_key, next) => {
value = next;
return { ok: true };
return { ok: /** @type {const} */ (true) };
},
remove: () => ({ ok: true }),
};
remove: () => ({ ok: /** @type {const} */ (true) }),
});
}
describe("controlled chunk recovery", () => {
+9 -5
View File
@@ -21,11 +21,15 @@ describe("color scheme policy", () => {
});
it("applies a persisted preference before application paint", () => {
const storage = {
read: () => ({ ok: true, value: "system" }),
write: () => ({ ok: true }),
remove: () => ({ ok: true }),
};
const storage =
/** @type {import("../../src/application/ports/storage-port.js").StoragePort} */ ({
read: () => ({
ok: /** @type {const} */ (true),
value: "system",
}),
write: () => ({ ok: /** @type {const} */ (true) }),
remove: () => ({ ok: /** @type {const} */ (true) }),
});
const result = initializeColorScheme(storage, {
documentElement: document.documentElement,
+8 -2
View File
@@ -39,14 +39,20 @@ describe("frontend failure classification", () => {
});
it("projects only allowlisted safe fields", () => {
const result = createFailure("SERVER_FAILURE", "LIST_SAMPLE_RESOURCES", 0, {
const untrustedDetails = {
code: "TEMPORARY",
httpStatus: 503,
requestId: "request-1",
stack: "must not leak",
body: "must not leak",
authorization: "Bearer secret",
});
};
const result = createFailure(
"SERVER_FAILURE",
"LIST_SAMPLE_RESOURCES",
0,
untrustedDetails,
);
expect(result).toMatchObject({
kind: "SERVER_FAILURE",
+9 -2
View File
@@ -6,7 +6,10 @@ import {
} from "../../src/application/policies/promotion-readiness.js";
const allGateIds = Object.values(PROMOTION_FORMULA).flat();
const passing = Object.fromEntries(allGateIds.map((gateId) => [gateId, "PASS"]));
const passing =
/** @type {Record<string, "PASS" | "FAIL" | "UNVERIFIED">} */ (
Object.fromEntries(allGateIds.map((gateId) => [gateId, "PASS"]))
);
describe("promotion readiness formula", () => {
it("requires every upstream tier before downstream readiness", () => {
@@ -30,7 +33,11 @@ describe("promotion readiness formula", () => {
...passing,
[failedGate]: "FAIL",
});
expect(result[readiness]).toBe(false);
const readinessKey =
/** @type {keyof ReturnType<typeof evaluatePromotionReadiness>} */ (
readiness
);
expect(result[readinessKey]).toBe(false);
});
it("does not treat missing or unverified gates as pass", () => {
+6 -2
View File
@@ -6,11 +6,15 @@ describe("registry governance manifest", () => {
const governance = JSON.parse(
await readFile("config/contracts/registry-governance.json", "utf8"),
);
const registries =
/** @type {Array<{registryId: string, owner: string}>} */ (
governance.registries
);
expect(governance.registries).toHaveLength(8);
expect(new Set(governance.registries.map((entry) => entry.registryId)).size).toBe(
expect(new Set(registries.map((entry) => entry.registryId)).size).toBe(
8,
);
expect(governance.registries.every((entry) => entry.owner)).toBe(true);
expect(registries.every((entry) => entry.owner)).toBe(true);
expect(governance.compatibilityImpact.allowed).toEqual([
"none",
"additive",
+2 -2
View File
@@ -10,7 +10,7 @@ const runtime = {
AUTH_MODE: "demo",
},
};
const release = {
const release = /** @type {const} */ ({
schemaVersion: 1,
appVersion: "0.1.0",
buildId: "build-a",
@@ -20,7 +20,7 @@ const release = {
assetManifestHash: "hash-a",
releaseId: "release-a",
builtAt: "2026-07-25T00:00:00Z",
};
});
describe("runtime adapter composition", () => {
it("constructs the local demo seam and infrastructure adapters", async () => {
+5 -1
View File
@@ -7,8 +7,12 @@ import {
defineStorageKey,
} from "../../src/contracts/storage-keys.js";
/**
* @param {{quota?: boolean}} [options]
* @returns {Storage}
*/
function createStorage({ quota = false } = {}) {
const values = new Map();
const values = /** @type {Map<string, string>} */ (new Map());
return {
getItem: (key) => values.get(key) ?? null,
setItem: (key, value) => {
+1 -1
View File
@@ -56,7 +56,7 @@ describe("telemetry registry and redaction", () => {
describe("best-effort telemetry adapter", () => {
it("bounds the queue using oldest-drop without blocking callers", () => {
const scheduled = [];
const scheduled = /** @type {Array<() => void>} */ ([]);
const adapter = createTelemetryAdapter({
enabled: true,
endpoint: "https://telemetry.test/events",
@@ -0,0 +1,16 @@
import { describe, expect, it } from "vitest";
type Result<T, E> =
| Readonly<{ ok: true; value: T }>
| Readonly<{ ok: false; error: E }>;
function summarize(result: Result<number, string>): string {
return result.ok ? String(result.value) : result.error;
}
describe("TypeScript test tooling", () => {
it("type-checks and executes discriminated unions in the test project", () => {
expect(summarize({ ok: true, value: 42 })).toBe("42");
expect(summarize({ ok: false, error: "failed" })).toBe("failed");
});
});