feat: ratchet hand-rolled abort wiring so it can only shrink
커널이 있는데도 어댑터 24개 파일이 addEventListener("abort")로 같은
race/cleanup을 각자 짠다. 전수 이행은 abort 의미론을 바꾸는 별도 작업이라
한 번에 하지 않고, 개수를 게이트에 고정해 되돌아가지 못하게 한다.
파일을 하나 더 늘려 실제로 막히는 것을 확인했다.
REQUIRED_ABORT_CONSUMERS 주석도 고친다. 그 목록은 재검토가 이름으로 지목한
네 파일만 덮는데, 주석은 Image와 Resumable을 지목하면서 정작 그 두 그룹의
런타임 파일(image-cdn-runtime.ts, resumable-upload-runtime.ts)은 목록에 없어
게이트가 초록불인 채로 사본이 유지되고 있었다.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5d9ad4f74a
commit
d777fb84b0
@@ -130,6 +130,12 @@ async function main(): Promise<void> {
|
||||
// let an unrelated production import satisfy the gate while Image and
|
||||
// Resumable kept their own diverging copies of the same mechanics — which is
|
||||
// exactly how the four hand-written versions drifted apart in the first place.
|
||||
//
|
||||
// 이 목록은 재검토가 이름으로 지목한 네 파일만 덮는다. 같은 두 그룹의
|
||||
// `image-cdn-runtime.ts`와 `resumable-upload-runtime.ts`는 여기 없고 지금도
|
||||
// 자기 abort 사본을 들고 있다. 어댑터 전체로는 24개 파일이 그렇다. 그 전수
|
||||
// 이행은 abort 의미론을 바꾸는 별도 작업이라 이 목록으로 강제하지 않고,
|
||||
// 아래 래칫이 개수가 늘어나는 것만 막는다.
|
||||
const REQUIRED_ABORT_CONSUMERS: readonly string[] = [
|
||||
"src/adapters/browser-transfer/presigned/presigned-capability-http-provider.ts",
|
||||
"src/adapters/browser-transfer/presigned/presigned-transfer-executor.ts",
|
||||
@@ -164,6 +170,7 @@ async function main(): Promise<void> {
|
||||
const PRIMITIVE_PATH = path.resolve(
|
||||
"src/adapters/platform/abortable-operation.ts",
|
||||
);
|
||||
const PRIMITIVE_SPECIFIER = "platform/abortable-operation.ts";
|
||||
for (const consumer of REQUIRED_ABORT_CONSUMERS) {
|
||||
if (!importerSet.has(consumer)) continue;
|
||||
const source = readFileSync(consumer, "utf8");
|
||||
@@ -181,6 +188,37 @@ async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// 손수 짠 abort 배선은 줄어들기만 해야 한다.
|
||||
//
|
||||
// 커널 `platform/abortable-operation.ts`가 있는데도 어댑터 24개 파일이
|
||||
// `addEventListener("abort")`로 같은 race/cleanup을 각자 짠다. 그 전수 이행은
|
||||
// 동작이 바뀌는 큰 작업이라 한 번에 하지 않는다. 대신 개수를 여기 고정해
|
||||
// 되돌아가지 못하게 한다. 이행으로 숫자가 내려가면 이 상수도 같이 내린다.
|
||||
// `platform/`은 커널 자신이므로 세지 않는다.
|
||||
const HAND_ROLLED_ABORT_CEILING = 24;
|
||||
const handRolledScan = spawnSync(
|
||||
"git",
|
||||
["grep", "-l", 'addEventListener("abort"', "--", "src/adapters"],
|
||||
{ encoding: "utf8" },
|
||||
);
|
||||
const handRolledFiles = (
|
||||
handRolledScan.status === 0 ? handRolledScan.stdout : ""
|
||||
)
|
||||
.split("\n")
|
||||
.filter(Boolean)
|
||||
.filter((file) => !file.startsWith("src/adapters/platform/"))
|
||||
.filter((file) => !readFileSync(file, "utf8").includes(PRIMITIVE_SPECIFIER))
|
||||
.sort();
|
||||
if (handRolledFiles.length > HAND_ROLLED_ABORT_CEILING) {
|
||||
problems.push(
|
||||
`abortable-operation: hand-rolled abort wiring grew to ` +
|
||||
`${handRolledFiles.length} files (ceiling ${HAND_ROLLED_ABORT_CEILING}). ` +
|
||||
`Use platform/abortable-operation.ts instead of a new listener pair. ` +
|
||||
`Current offenders (the new one is whichever this change added): ` +
|
||||
`${handRolledFiles.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (problems.length > 0) {
|
||||
for (const problem of problems) console.error(problem);
|
||||
process.exitCode = 1;
|
||||
@@ -195,7 +233,9 @@ async function main(): Promise<void> {
|
||||
} shared extensions PASS; ` +
|
||||
`fixture node_modules linking PASS; ` +
|
||||
`shared abort primitive: ${importers.length} importers ` +
|
||||
`(${importers.join(", ")}) PASS`,
|
||||
`(${importers.join(", ")}) PASS; ` +
|
||||
`hand-rolled abort wiring: ${handRolledFiles.length}/` +
|
||||
`${HAND_ROLLED_ABORT_CEILING} files (ratchet) PASS`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user