feat: add the TechLog asset multipart upload transport

Wires the whole Asset capability into the running application: the
multipart upload transport (the contract runtime can only express JSON
bodies), a single composition-root-owned CSRF provider shared between
the platform's credential collaborator (18 JSON operations) and the
upload transport (1 multipart operation), and Studio/StudioShell
exposure of the Asset gateway alongside the existing document gateway.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-08-18 02:39:51 +09:00
co-authored by Claude Opus 5
parent d9c2d8bc5e
commit c9c832c365
15 changed files with 473 additions and 3 deletions
@@ -4,6 +4,7 @@ import { test } from "vitest";
import type { ApplicationFeatureInputs } from "../../../src/application/ports/in/application-api.ts";
import { createInstalledFeatureInputs } from "../../../src/features/installed-feature-adapters.ts";
import { FIXTURE_IDS } from "../../../src/features/tech-log/adapters/mock/fixtures.ts";
import { createCsrfTokenProvider } from "../../../src/features/tech-log/adapters/http/studio-session-csrf.ts";
import type { TechLogFeatureInput } from "../../../src/features/tech-log/application/tech-log-feature-input.ts";
import type { WorkingCopy } from "../../../src/features/tech-log/contracts/studio/contract.ts";
@@ -42,6 +43,13 @@ function installedInputs(
throw new Error("reference executor is not used by composition tests");
},
},
apiBaseUrl: "http://composition.test/",
requestTimeoutMs: 10_000,
csrf: createCsrfTokenProvider({
async execute() {
throw new Error("CSRF provider is not used by composition tests");
},
}),
});
}
@@ -50,6 +58,7 @@ test("installs TechLog beside the retained reference feature through application
assert.deepEqual(Object.keys(installed), ["reference-feature", "tech-log"]);
assert.deepEqual(Object.keys(installed["tech-log"]).sort(), [
"createStudioAssetGateway",
"createStudioGateway",
"publicContent",
]);
@@ -107,3 +116,11 @@ test("each createStudioGateway call owns an isolated mutable Studio session", as
"컬렉션 Fetch Join과 페이징은 왜 충돌하는가",
);
});
test("each Studio session gets its own asset gateway instance", () => {
const installed = installedInputs("HTTP");
assert.notEqual(
installed["tech-log"].createStudioAssetGateway(),
installed["tech-log"].createStudioAssetGateway(),
);
});