chore(types): compile cleanly under the TypeScript 7 native compiler (#16463)

* fix(types): compile cleanly under the TypeScript 7 native compiler

tsgo (@typescript/native-preview) reported 48 errors across the
typecheck projects while tsc 5.8 reported none. This removes all of
them without changing what tsc emits, so a tsgo typecheck lane or a
later compiler switch can start from a clean tree.

- Drop `baseUrl` from five tsconfigs. TypeScript 7 removed it, and
  every `paths` target here is already relative to its tsconfig.
- Set `rootDir` where TypeScript 7 no longer infers it (web-shell
  declarations, sdk-typescript), and list node types explicitly for
  cua-driver now that @types are no longer included by default.
- Annotate Buffers built by Node's Buffer factories as
  `Buffer<ArrayBuffer>` (or widen to `Buffer` where a variable later
  receives stream chunks); the ES2024 lib no longer treats
  ArrayBufferLike as an ArrayBuffer, so these failed at fetch, Blob,
  and Response call sites. The two call sites that take caller- or
  interface-supplied bytes assert `BodyInit`, matching the existing
  XMLHttpRequest upload path.
- Remove three redundant `?? []` / `?? undefined` operands that the
  newer checker reports as unreachable.
- Declare `*.css` modules for the two integrations that import
  stylesheets, now that side-effect imports are checked.

* fix(types): include web-shell client declarations in the build config

The config's include listed only client/index.tsx, so client/vite-env.d.ts
and client/local-files/file-system-access.d.ts never entered the program.
Removing baseUrl unmasked 227 tsgo errors (and 222 under tsc 5.8) for the
CSS module and File System Access declarations they provide.

---------

Co-authored-by: Shaojin Wen <[email protected]>
This commit is contained in:
chencheng (云谦)andShaojin Wen authored and GitHub committed 2026-11-07 08:23:16 +00:00
1 parent f2e9a3fc66
commit 29fa7bbe06
30 files changed
+51 -28

No files matched your search

+2 -2
View File
@@ -38,10 +38,10 @@ function inspectTodoRequests(requests: Array<Record<string, unknown>>) {
const toolResult = requests
.flatMap(
(request) =>
((request?.['messages'] ?? []) as Array<{
(request?.['messages'] ?? []) as Array<{
role?: string;
content?: unknown;
}>) ?? [],
}>,
)
.filter(({ role }) => role === 'tool')
.map(({ content }) => JSON.stringify(content))
@@ -57,7 +57,7 @@ async function json(route: string, body?: unknown, expected = 200) {
return text ? JSON.parse(text) : undefined;
}
async function upstream(route: string, body?: Buffer) {
async function upstream(route: string, body?: Buffer<ArrayBuffer>) {
const response = await fetch(new URL(route, config.brokerUrl), {
method: body?.length ? 'POST' : 'GET',
headers: {
@@ -68,7 +68,7 @@ let injected = false;
let serviceKilled = false;
let proxyFailure: unknown;
let originalCancel:
| { path: string; headers: Record<string, string>; body: Buffer }
| { path: string; headers: Record<string, string>; body: Buffer<ArrayBuffer> }
| undefined;
let releaseFollowupModel!: () => void;
const followupModel = new Promise<void>((resolve) => {
-1
View File
@@ -18,7 +18,6 @@
// `terminal-capture/` and pulls SDK sources that reference `WebSocket` /
// `HeadersInit`, none of which exist in the root's ES2023-only lib.
"lib": ["DOM", "DOM.Iterable", "ES2023"],
"baseUrl": ".",
// Resolve workspace packages from source so `tsc -p` here does not depend
// on whether someone had built them recently — a missing dist used to fail
// resolution outright and a stale one silently typechecked against old
+8
View File
@@ -0,0 +1,8 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
// The bundler inlines stylesheet imports; this lets TypeScript accept them.
declare module '*.css';
+8
View File
@@ -0,0 +1,8 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
// The bundler inlines stylesheet imports; this lets TypeScript accept them.
declare module '*.css';
-1
View File
@@ -3,7 +3,6 @@
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"baseUrl": ".",
"lib": ["ES2023"],
"composite": true,
"types": ["node", "vitest/globals"],
@@ -210,7 +210,7 @@ function channelImageName(mimeType: string, index = 0): string | undefined {
function decodeChannelImage(
data: string,
oversizedReason: string,
): { bytes: Buffer } | { skip: string } {
): { bytes: Buffer<ArrayBuffer> } | { skip: string } {
// Valid base64 decodes to at most this many bytes, so an oversized image
// is rejected on length alone instead of allocating a buffer the size
// check would discard. Padding is subtracted only when the input length
@@ -29,7 +29,7 @@ export interface FileProjection {
}
export interface ValidatedFile {
data: Buffer;
data: Buffer<ArrayBuffer>;
fileName: string;
fileType: string;
}
@@ -22,7 +22,7 @@ export interface ImageMarker {
}
export interface ValidatedImage {
data: Buffer;
data: Buffer<ArrayBuffer>;
fileName: string;
mimeType: string;
}
+1 -1
View File
@@ -338,7 +338,7 @@ export async function getUploadUrl(
export async function uploadToCdn(
urlOrParam: string,
filekey: string,
encryptedData: Buffer,
encryptedData: Buffer<ArrayBuffer>,
): Promise<string> {
const CDN_HOST = 'novac2c.cdn.weixin.qq.com';
+4 -1
View File
@@ -63,7 +63,10 @@ export async function downloadAndDecrypt(
}
/** AES-128-ECB encryption for CDN upload. */
export function encryptAesEcb(plaintext: Buffer, key: Buffer): Buffer {
export function encryptAesEcb(
plaintext: Buffer,
key: Buffer,
): Buffer<ArrayBuffer> {
const cipher = createCipheriv('aes-128-ecb', key, null);
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
}
@@ -669,7 +669,7 @@ export function createAgentViewPtyHostServer(
});
// Byte-transparent framing: only the request line is decoded, so
// coalesced keystrokes after an attach request reach the worker verbatim.
let buffer = Buffer.alloc(0);
let buffer: Buffer = Buffer.alloc(0);
socket.on('data', (chunk: Buffer) => {
buffer = buffer.length === 0 ? chunk : Buffer.concat([buffer, chunk]);
if (buffer.length > MAX_PTY_HOST_REQUEST_LINE_BYTES) {
@@ -306,7 +306,7 @@ export class ManagedExternalPublication
READ_BYTES,
Math.max(0, stream.byteLength - offset),
);
let bytes = Buffer.alloc(0);
let bytes: Buffer = Buffer.alloc(0);
if (length) {
const result = await channel.store.readRange({
manifestRef: manifest.ref,
@@ -176,7 +176,7 @@ class PublicationClient {
'Cache-Control': 'no-store',
...headers,
},
...(body ? { body } : {}),
...(body ? { body: body as BodyInit } : {}),
redirect: 'error',
signal: AbortSignal.timeout(30_000),
});
@@ -1046,11 +1046,11 @@ export function projectSpecialItemText(
);
case 'tools_list':
return projectToolsList(
((record['tools'] ?? []) as Array<{
(record['tools'] ?? []) as Array<{
name: string;
displayName?: string;
description?: string;
}>) ?? [],
}>,
Boolean(record['showDescriptions']),
);
case 'model_stats': {
-1
View File
@@ -3,7 +3,6 @@
"compilerOptions": {
"outDir": "dist",
"jsx": "react-jsx",
"baseUrl": ".",
"lib": ["DOM", "DOM.Iterable", "ES2023"],
"types": ["node", "vitest/globals"],
"paths": {
@@ -8028,7 +8028,7 @@ class FakeManagedSessionStore {
private readonly resources = new Map<
string,
{
bytes: Buffer;
bytes: Buffer<ArrayBuffer>;
kind: string;
schemaVersion: number;
digest: string;
+1 -1
View File
@@ -125,7 +125,7 @@ const respondWith = (body: BodyInit | null, init?: ResponseInit) =>
vi.fn(async () => new Response(body, init)) as unknown as typeof fetch;
function fetchOk(
body: Buffer | string,
body: Buffer<ArrayBuffer> | string,
headers: Record<string, string> = {},
): typeof fetch {
return vi.fn(async () => new Response(body, { status: 200, headers }));
@@ -203,7 +203,9 @@ export function createToolResultBoundaryObserver(
(logger === boundaryLogger
? isToolResultBoundaryDiagnosticsEnabled
: () => isDebugLogFileEnabled() && logger.isEnabled());
let hmacKey = options.hmacKey ? Buffer.from(options.hmacKey) : undefined;
let hmacKey: Buffer | undefined = options.hmacKey
? Buffer.from(options.hmacKey)
: undefined;
const thresholdBytes =
options.thresholdBytes ?? TOOL_RESULT_BOUNDARY_JSON_BYTE_THRESHOLD;
const logLimit = options.logLimit ?? TOOL_RESULT_BOUNDARY_LOG_LIMIT;
+1 -1
View File
@@ -214,7 +214,7 @@ export function streamGitRecords(
}
return true;
};
let pending = Buffer.alloc(0);
let pending: Buffer = Buffer.alloc(0);
child.stdout.on('data', (chunk: Buffer) => {
if (settled) return;
pending = pending.length ? Buffer.concat([pending, chunk]) : chunk;
+2 -1
View File
@@ -10,7 +10,8 @@
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true,
"verbatimModuleSyntax": true
"verbatimModuleSyntax": true,
"types": ["node"]
},
"include": ["src/**/*.ts"]
}
@@ -2676,7 +2676,7 @@ export class DaemonClient {
{
method: 'POST',
headers,
body: req.data,
body: req.data as BodyInit,
...(req.signal ? { signal: req.signal } : {}),
},
async (res) => {
@@ -2,6 +2,7 @@
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"rootDir": "./src",
"module": "ESNext",
"moduleResolution": "bundler",
"declaration": true,
+4 -1
View File
@@ -11,6 +11,10 @@
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
// The core sources mapped in `paths` below sit outside this package, so the
// project root is the packages directory. TypeScript 5 infers that; newer
// compilers default rootDir to this directory and reject those files.
"rootDir": "..",
"removeComments": true,
"importHelpers": false,
@@ -35,7 +39,6 @@
/* Module Resolution */
"resolveJsonModule": true,
"types": ["node"],
"baseUrl": ".",
// The cli bundle pulls these sources in through the cli tsconfig
// `@qwen-code/sdk/*` mapping, and mainBuild discovers this file per
// importing file (it passes no `tsconfig` option). Without a named entry
@@ -287,7 +287,7 @@ export async function readWorkspaceFileAsBlob(
maxBytes?: number;
},
): Promise<Blob> {
const chunks: Uint8Array[] = [];
const chunks: Array<Uint8Array<ArrayBuffer>> = [];
const maxBytes = options.maxBytes ?? MAX_WORKSPACE_FILE_BLOB_BYTES;
const initialStat = await options.statFile(filePath);
if (options.isCancelled?.()) {
@@ -212,7 +212,7 @@ function getProviderTextValue(provider: WebShellAtProvider): string {
? undefined
: sanitizeDisplayText(provider.textValue)) ??
(typeof provider.label === 'string'
? (sanitizeDisplayText(provider.label) ?? undefined)
? sanitizeDisplayText(provider.label)
: undefined) ??
safeDisplayText(provider.id) ??
SAFE_DISPLAY_FALLBACK
+1 -1
View File
@@ -7,5 +7,5 @@
"outDir": "dist",
"rootDir": "client"
},
"include": ["client/index.tsx"]
"include": ["client/index.tsx", "client/**/*.d.ts"]
}
-1
View File
@@ -4,7 +4,6 @@
"module": "ESNext",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"baseUrl": ".",
"paths": {
"@qwen-code/web-shell/daemon-react-sdk": ["./client/daemon-react-sdk.ts"],
"@qwen-code/web-shell/transcript": ["./client/transcript.ts"],
+1
View File
@@ -5,6 +5,7 @@
"stripInternal": true,
"emitDeclarationOnly": true,
"declarationDir": "dist/types",
"rootDir": "client",
"noEmit": false
},
"include": ["client/**/*.ts", "client/**/*.tsx"],