Files
rea/src/javascript/sourceMaps/SourceMapDecoder.ts
T
ysopwnable f430c416c7 feat(browser): trace captured website source-map locations (#897)
* refactor(browser): share captured-script acquisition boundaries

Reuse stable capture/source acquisition and identity validation for module
and source-map analysis. Preserve host I/O, malformed input, unavailable
source and digest mismatch diagnostics.

Refs #851

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* feat(browser): trace captured website source-map locations

Refs #851. Preserve selected capture/map byte identity, duplicate mappings and embedded source evidence through the pinned upstream codec.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* test(browser): verify source-map artifacts and synchronize generated contracts

Cover byte-preserving map reads and actionable filesystem constraints. Refresh generated public schemas and metadata on current main.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)

* fix(browser): preserve inert source-map URL context

The selected URL is local codec context and grants no transport authority. Preserve its reported text and verify normal/userinfo contexts through real public interfaces, alongside independent manifest byte identity.

Assisted-by: Yukari (gensokyo-skills:yukari-boundary-analysis)
Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-07 16:21:18 +08:00

125 lines
4.1 KiB
TypeScript

import type { ExecutionOptions } from "../../application/AnalysisProvider.js";
import type { WebSourceMapPort } from "../../application/WebSourceLocationPorts.js";
import type { AnalysisError } from "../../domain/analysisErrorBase.js";
import {
AnalysisCancelledError,
AnalysisCapabilityUnavailableError,
AnalysisInputError,
AnalysisOutputError,
} from "../../domain/analysisErrorCore.js";
import { ArtifactOperationError } from "../../domain/artifactOperationError.js";
import { err, ok, type Result } from "../../domain/result.js";
import {
WEB_SOURCE_MAP_LIMITS,
type WebSourceMapReport,
} from "../../domain/webSourceLocation.js";
import {
sourceMapCodecInputSchema,
sourceMapCodecReplySchema,
} from "./SourceMapCodecProtocol.js";
import {
runSourceMapCommand,
type SourceMapDecoderDependencies,
} from "./SourceMapCommand.js";
export type { SourceMapDecoderDependencies } from "./SourceMapCommand.js";
const OPERATION = "trace_web_source_location";
/** Integrate the pinned upstream codec through an independently bounded owned process. */
export class SourceMapDecoder implements WebSourceMapPort {
constructor(readonly dependencies: SourceMapDecoderDependencies = {}) {}
/** Return complete point evidence after process and private-root cleanup. */
async trace(
input: Parameters<WebSourceMapPort["trace"]>[0],
options?: ExecutionOptions,
): Promise<Result<WebSourceMapReport, AnalysisError>> {
if (options?.signal?.aborted)
return err(new AnalysisCancelledError(OPERATION));
const request = sourceMapCodecInputSchema.safeParse({
text: input.text,
url: input.url,
position: input.position,
});
if (!request.success)
return err(new AnalysisInputError(OPERATION, { cause: request.error }));
const serialized = JSON.stringify(request.data);
if (
Buffer.byteLength(input.text) > WEB_SOURCE_MAP_LIMITS.mapBytes ||
Buffer.byteLength(serialized) > WEB_SOURCE_MAP_LIMITS.outputBytes
)
return err(
new ArtifactOperationError(
OPERATION,
"limit",
undefined,
`${input.path}: Source-map input exceeds the 4 MiB map or 32 MiB encoded request budget.`,
),
);
const response = await runSourceMapCommand(
serialized,
input.path,
options,
this.dependencies,
);
if (!response.ok) return response;
let value: unknown;
try {
value = JSON.parse(response.value);
} catch (cause: unknown) {
return err(
new AnalysisOutputError(
OPERATION,
`Malformed codec JSON reply: ${cause instanceof Error ? cause.message : String(cause)}`,
),
);
}
const reply = sourceMapCodecReplySchema.safeParse(value);
if (!reply.success)
return err(
new AnalysisOutputError(
OPERATION,
`Malformed codec reply: ${reply.error.message}`,
),
);
if (options?.signal?.aborted)
return err(new AnalysisCancelledError(OPERATION));
if (reply.data.state === "failure")
return codecFailure(input.path, reply.data.reason, reply.data.message);
if (reply.data.report.runtime.v8_heap_limit_bytes > 256 * 1024 * 1024)
return err(
new AnalysisOutputError(
OPERATION,
"Codec reported a V8 heap limit exceeding its independent process budget.",
),
);
return ok(reply.data.report);
}
}
const codecFailure = (
path: string,
reason: "format" | "unsupported" | "limit",
detail: string,
): Result<never, AnalysisError> =>
err(
reason === "limit"
? new ArtifactOperationError(
OPERATION,
"limit",
undefined,
`${path}: ${detail}`,
)
: reason === "unsupported"
? new AnalysisCapabilityUnavailableError(
"source-map-decoder",
OPERATION,
"unsupported_source_map_profile",
{ userMessage: `${path}: ${detail}` },
)
: new AnalysisInputError(OPERATION, undefined, [
{
path: ["source_map", "path"],
reason: "invalid_format",
message: `${path}: ${detail}`,
},
]),
);