[GH-ISSUE #3945] [Bug] Query-bearing TypeScript source URLs receive JavaScript media types #1554

Closed
opened 2026-10-16 21:07:04 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/3945

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

analyze-web-bundle --fetch-source-maps returns embedded original sources with text/javascript media types when their URL paths end in .ts, .tsx, .mts, or .cts and their declarations include a query. A plain .ts source receives the documented text/typescript label. The observed defect affects language metadata; source text and resolved URLs remain intact.

Reproduction

Use the baseline under Runtime or environment with its dependencies installed and runtime already built. The reproduction uses the repository's fake browser through real loopback HTTP/WebSocket connections and runs the built CLI. It needs no external network service or installed browser. I executed this fixture and CLI request through a focused test; the equivalent standalone reproduction below isolates HOME for the test process and CLI child.

  1. Create and run the following temporary diagnostic test from the repository root, then remove it and the temporary HOME.
cat > tests/boundary/cli/source-media-type.repro.test.ts <<'TEST'
import { expect, it } from "vitest";
import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js";
import { runCliJson } from "../../fixtures/cliJsonProcess.js";

it("prints source-map original-source media types from the CLI", async () => {
  const sources = [
    "main.ts?revision=2",
    "plain.ts",
    "main.tsx?revision=2",
    "main.mts?revision=2",
    "main.cts?revision=2",
    "main.js?revision=2",
    "extensionless?revision=2",
  ];
  const text = "export const value = 1;\n";
  const browser = await startFakeCdpBrowser({
    sourceMapBody: JSON.stringify({
      version: 3,
      sources,
      sourcesContent: sources.map(() => text),
      names: [],
      mappings: "AAAA",
    }),
  });
  try {
    const evidence: any = await runCliJson([
      "analyze-web-bundle", browser.endpoint, "allowed-page",
      "--allowed-origins", browser.allowedOrigin,
      "--observation-ms", "0", "--fetch-source-maps", "--json",
    ]);
    const maps = evidence.normalized_result.observations.source_maps;
    expect(maps.status).toBe("included");
    const originals = maps.items[0].original_sources;
    console.log(JSON.stringify(originals.map((source: any) => ({
      source: source.source.replace(browser.allowedOrigin, "ORIGIN"),
      media_type: source.artifact.media_type,
    })), null, 2));
    expect(originals.every((source: any) => source.artifact.text === text)).toBe(true);
    expect(originals.map((source: any) => {
      const url = new URL(source.source);
      return url.pathname + url.search;
    })).toEqual(sources.map(source => "/" + source));
    expect(originals.map((source: any) => source.artifact.media_type)).toEqual([
      "text/javascript", "text/typescript", "text/javascript",
      "text/javascript", "text/javascript", "text/javascript", "text/javascript",
    ]);
  } finally {
    await browser.close();
  }
});
TEST
probe_home="$(mktemp -d)"
HOME="$probe_home" npx vitest run tests/boundary/cli/source-media-type.repro.test.ts
probe_status=$?
rm tests/boundary/cli/source-media-type.repro.test.ts
rm -r "$probe_home"
exit "$probe_status"
  1. Inspect the printed media_type for each source. This diagnostic test asserts the baseline's incorrect labels so that its pass confirms the reported observation, not restoration of correct behavior.

Expected behavior

Original source artifacts whose URL paths have one of the documented TypeScript suffixes should carry text/typescript, including when a query follows the path. The original-source parser already derives language semantics from the URL pathname, excluding query and fragment. A query-bearing .js source and an extensionless source should retain text/javascript. Source text and URL queries should remain intact.

docs/browser-observation.md:358 documents the TypeScript suffix/media-type rule. The sentence does not explicitly discuss queries; the pathname-based parser at src/browser/WebSourceMapFetcher.ts:750-753,805-814 supports treating the language suffix as part of the source path. The later map-URL query-preservation paragraph does not independently promise original-source media types.

Impact and scope

P3. The observed result is an incorrect language label for query-bearing TypeScript original sources. All source text and resolved URLs remained correct; no parser failure or consequential downstream effect was established. Callers can inspect the returned URL pathname to identify the suffix while retaining the full artifact text.

REA version

rea 6.5.0 at source commit 4692bb3330

Runtime and platform

Node 24.18.0; macOS arm64

Target and artifact context

Version-3 source map with embedded TypeScript, JavaScript, and extensionless original sources served by the loopback fake CDP browser. Built analyze-web-bundle CLI with source-map fetching enabled; no real browser or external provider.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

I observed two successful CLI reproductions with the same map fixture and source maps reported as included. Query-bearing .ts, .tsx, .mts, and .cts declarations all returned text/javascript; plain .ts returned text/typescript. JavaScript and extensionless controls retained text/javascript. All seven returned artifacts preserved their source text and resolved source paths/queries. The relevant output was:

[
  { "source": "ORIGIN/main.ts?revision=2", "media_type": "text/javascript" },
  { "source": "ORIGIN/plain.ts", "media_type": "text/typescript" },
  { "source": "ORIGIN/main.tsx?revision=2", "media_type": "text/javascript" },
  { "source": "ORIGIN/main.mts?revision=2", "media_type": "text/javascript" },
  { "source": "ORIGIN/main.cts?revision=2", "media_type": "text/javascript" },
  { "source": "ORIGIN/main.js?revision=2", "media_type": "text/javascript" },
  { "source": "ORIGIN/extensionless?revision=2", "media_type": "text/javascript" }
]

ORIGIN replaces the fake browser's dynamically allocated loopback origin. Source inspection explains the result: artifact media types use the raw source declaration's suffix, while original-source parsing uses the resolved URL pathname. No parser failure or broader downstream consequence was observed.

Typed outcome or recovery expected

Repeat the same supported CLI request and map fixture. Each query-bearing TypeScript source and the plain TypeScript control must return text/typescript. The JavaScript and extensionless controls must keep text/javascript, and all source text and resolved URLs, including queries, must remain intact. Change the diagnostic's expected media-type array to that passing result when verifying a fix. The choice of normalization or implementation remains open.

Analysis or suspected cause

Source-derived: src/browser/WebSourceMapFetcher.ts:592-624 passes the raw declaration to artifact media-type selection, and lines 1059-1066 check its raw suffix. The original-source parser separately uses the resolved URL pathname at lines 750-753 and 805-814, excluding the query and fragment.

Additional context

The cited failing code is unchanged on current origin/main at 22ec8bdb2f7d4626587f23b1eed4a207df600386. Related closed #2954 concerns parser-mode selection from original filenames and has been fixed; this report concerns the separate artifact media-type selector, with no observed parser failure.

Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3945 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior `analyze-web-bundle --fetch-source-maps` returns embedded original sources with `text/javascript` media types when their URL paths end in `.ts`, `.tsx`, `.mts`, or `.cts` and their declarations include a query. A plain `.ts` source receives the documented `text/typescript` label. The observed defect affects language metadata; source text and resolved URLs remain intact. ### Reproduction Use the baseline under Runtime or environment with its dependencies installed and runtime already built. The reproduction uses the repository's fake browser through real loopback HTTP/WebSocket connections and runs the built CLI. It needs no external network service or installed browser. I executed this fixture and CLI request through a focused test; the equivalent standalone reproduction below isolates HOME for the test process and CLI child. 1. Create and run the following temporary diagnostic test from the repository root, then remove it and the temporary HOME. ```sh cat > tests/boundary/cli/source-media-type.repro.test.ts <<'TEST' import { expect, it } from "vitest"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; import { runCliJson } from "../../fixtures/cliJsonProcess.js"; it("prints source-map original-source media types from the CLI", async () => { const sources = [ "main.ts?revision=2", "plain.ts", "main.tsx?revision=2", "main.mts?revision=2", "main.cts?revision=2", "main.js?revision=2", "extensionless?revision=2", ]; const text = "export const value = 1;\n"; const browser = await startFakeCdpBrowser({ sourceMapBody: JSON.stringify({ version: 3, sources, sourcesContent: sources.map(() => text), names: [], mappings: "AAAA", }), }); try { const evidence: any = await runCliJson([ "analyze-web-bundle", browser.endpoint, "allowed-page", "--allowed-origins", browser.allowedOrigin, "--observation-ms", "0", "--fetch-source-maps", "--json", ]); const maps = evidence.normalized_result.observations.source_maps; expect(maps.status).toBe("included"); const originals = maps.items[0].original_sources; console.log(JSON.stringify(originals.map((source: any) => ({ source: source.source.replace(browser.allowedOrigin, "ORIGIN"), media_type: source.artifact.media_type, })), null, 2)); expect(originals.every((source: any) => source.artifact.text === text)).toBe(true); expect(originals.map((source: any) => { const url = new URL(source.source); return url.pathname + url.search; })).toEqual(sources.map(source => "/" + source)); expect(originals.map((source: any) => source.artifact.media_type)).toEqual([ "text/javascript", "text/typescript", "text/javascript", "text/javascript", "text/javascript", "text/javascript", "text/javascript", ]); } finally { await browser.close(); } }); TEST probe_home="$(mktemp -d)" HOME="$probe_home" npx vitest run tests/boundary/cli/source-media-type.repro.test.ts probe_status=$? rm tests/boundary/cli/source-media-type.repro.test.ts rm -r "$probe_home" exit "$probe_status" ``` 2. Inspect the printed `media_type` for each source. This diagnostic test asserts the baseline's incorrect labels so that its pass confirms the reported observation, not restoration of correct behavior. ### Expected behavior Original source artifacts whose URL paths have one of the documented TypeScript suffixes should carry `text/typescript`, including when a query follows the path. The original-source parser already derives language semantics from the URL pathname, excluding query and fragment. A query-bearing `.js` source and an extensionless source should retain `text/javascript`. Source text and URL queries should remain intact. `docs/browser-observation.md:358` documents the TypeScript suffix/media-type rule. The sentence does not explicitly discuss queries; the pathname-based parser at `src/browser/WebSourceMapFetcher.ts:750-753,805-814` supports treating the language suffix as part of the source path. The later map-URL query-preservation paragraph does not independently promise original-source media types. ### Impact and scope P3. The observed result is an incorrect language label for query-bearing TypeScript original sources. All source text and resolved URLs remained correct; no parser failure or consequential downstream effect was established. Callers can inspect the returned URL pathname to identify the suffix while retaining the full artifact text. ### REA version rea 6.5.0 at source commit 4692bb33300c7cb77b095b6cc0c33da861c1b860 ### Runtime and platform Node 24.18.0; macOS arm64 ### Target and artifact context Version-3 source map with embedded TypeScript, JavaScript, and extensionless original sources served by the loopback fake CDP browser. Built `analyze-web-bundle` CLI with source-map fetching enabled; no real browser or external provider. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence I observed two successful CLI reproductions with the same map fixture and source maps reported as `included`. Query-bearing `.ts`, `.tsx`, `.mts`, and `.cts` declarations all returned `text/javascript`; plain `.ts` returned `text/typescript`. JavaScript and extensionless controls retained `text/javascript`. All seven returned artifacts preserved their source text and resolved source paths/queries. The relevant output was: ```json [ { "source": "ORIGIN/main.ts?revision=2", "media_type": "text/javascript" }, { "source": "ORIGIN/plain.ts", "media_type": "text/typescript" }, { "source": "ORIGIN/main.tsx?revision=2", "media_type": "text/javascript" }, { "source": "ORIGIN/main.mts?revision=2", "media_type": "text/javascript" }, { "source": "ORIGIN/main.cts?revision=2", "media_type": "text/javascript" }, { "source": "ORIGIN/main.js?revision=2", "media_type": "text/javascript" }, { "source": "ORIGIN/extensionless?revision=2", "media_type": "text/javascript" } ] ``` `ORIGIN` replaces the fake browser's dynamically allocated loopback origin. Source inspection explains the result: artifact media types use the raw source declaration's suffix, while original-source parsing uses the resolved URL pathname. No parser failure or broader downstream consequence was observed. ### Typed outcome or recovery expected Repeat the same supported CLI request and map fixture. Each query-bearing TypeScript source and the plain TypeScript control must return `text/typescript`. The JavaScript and extensionless controls must keep `text/javascript`, and all source text and resolved URLs, including queries, must remain intact. Change the diagnostic's expected media-type array to that passing result when verifying a fix. The choice of normalization or implementation remains open. ### Analysis or suspected cause Source-derived: `src/browser/WebSourceMapFetcher.ts:592-624` passes the raw declaration to artifact media-type selection, and lines 1059-1066 check its raw suffix. The original-source parser separately uses the resolved URL pathname at lines 750-753 and 805-814, excluding the query and fragment. ### Additional context The cited failing code is unchanged on current `origin/main` at `22ec8bdb2f7d4626587f23b1eed4a207df600386`. Related closed #2954 concerns parser-mode selection from original filenames and has been fixed; this report concerns the separate artifact media-type selector, with no observed parser failure.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1554
No description provided.