From 7909a17a76b0b89d2a6b4c8c07f317205365aa52 Mon Sep 17 00:00:00 2001 From: coyaSONG <66289470+coyaSONG@users.noreply.github.com> Date: Fri, 17 Jul 2026 02:10:49 +0900 Subject: [PATCH 1/3] Fix published pre-response handler types --- .../fix-http-pre-response-handler-types.md | 5 ++ .../http/internal/preResponseHandler.ts | 1 - .../src/oxlint/rules/no-unused-internal.ts | 72 +++++++++++++++++-- .../tools/oxc/test/no-unused-internal.test.ts | 17 +++++ 4 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 .changeset/fix-http-pre-response-handler-types.md diff --git a/.changeset/fix-http-pre-response-handler-types.md b/.changeset/fix-http-pre-response-handler-types.md new file mode 100644 index 00000000000..66eeb8b702b --- /dev/null +++ b/.changeset/fix-http-pre-response-handler-types.md @@ -0,0 +1,5 @@ +--- +"effect": patch +--- + +Fix the published declaration for `HttpEffect.appendPreResponseHandlerUnsafe`. diff --git a/packages/effect/src/unstable/http/internal/preResponseHandler.ts b/packages/effect/src/unstable/http/internal/preResponseHandler.ts index 300c9c39a0c..07e781264f4 100644 --- a/packages/effect/src/unstable/http/internal/preResponseHandler.ts +++ b/packages/effect/src/unstable/http/internal/preResponseHandler.ts @@ -5,7 +5,6 @@ import type { HttpServerRequest } from "../HttpServerRequest.ts" /** @internal */ export const requestPreResponseHandlers = new WeakMap() -/** @internal */ export const appendPreResponseHandlerUnsafe = (request: HttpServerRequest, handler: PreResponseHandler): void => { const prev = requestPreResponseHandlers.get(request.source) const next: PreResponseHandler = prev ? diff --git a/packages/tools/oxc/src/oxlint/rules/no-unused-internal.ts b/packages/tools/oxc/src/oxlint/rules/no-unused-internal.ts index 55de8c187eb..981f673d752 100644 --- a/packages/tools/oxc/src/oxlint/rules/no-unused-internal.ts +++ b/packages/tools/oxc/src/oxlint/rules/no-unused-internal.ts @@ -286,6 +286,33 @@ function getExports( return fileInfos.get(fileName)?.internalExports.get(exportName) ?? [] } +function getReExportedInternals( + fileInfos: ReadonlyMap, + fileInfo: FileInfo, + workspacePackages: ReadonlyMap, + specifier: ts.ExportSpecifier +): ReadonlyArray { + const exportDeclaration = specifier.parent.parent + const importedName = (specifier.propertyName ?? specifier.name).text + if ( + exportDeclaration.moduleSpecifier !== undefined && + ts.isStringLiteral(exportDeclaration.moduleSpecifier) + ) { + const importedFile = resolveModule( + exportDeclaration.moduleSpecifier.text, + fileInfo.sourceFile.fileName, + workspacePackages + ) + return getExports(fileInfos, importedFile, importedName) + } + + const imported = fileInfo.imports.get(importedName) + return [ + ...(fileInfo.internalExports.get(importedName) ?? []), + ...getExports(fileInfos, imported?.fileName, imported?.importedName ?? "") + ] +} + function markUsed(exports: ReadonlyArray, node: ts.Node) { for (const internal of exports) { if (!isInside(node, internal.declaration)) { @@ -348,6 +375,17 @@ function collectFileInfo( continue } + if ( + !isInternalFile && + ts.isExportDeclaration(statement) && + !hasInternalApiJSDoc(statement) && + statement.exportClause !== undefined && + ts.isNamedExports(statement.exportClause) + ) { + fileInfo.publicDeclarations.push(statement) + continue + } + if (!hasExportModifier(statement)) continue const names = getTopLevelDeclarationNameNodes(statement) @@ -385,9 +423,15 @@ function markNamespaceReference( markUsed(getExports(fileInfos, importedFile, name.text), name) } -function scanUsage(fileInfos: ReadonlyMap, fileInfo: FileInfo) { +function scanUsage( + fileInfos: ReadonlyMap, + fileInfo: FileInfo, + workspacePackages: ReadonlyMap +) { const visit = (node: ts.Node) => { - if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && ts.isIdentifier(node.name)) { + if (ts.isExportSpecifier(node)) { + markUsed(getReExportedInternals(fileInfos, fileInfo, workspacePackages, node), node) + } else if (ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) && ts.isIdentifier(node.name)) { markNamespaceReference(fileInfos, fileInfo, node.expression, node.name) } else if (ts.isQualifiedName(node) && ts.isIdentifier(node.left)) { markNamespaceReference(fileInfos, fileInfo, node.left, node.right) @@ -529,7 +573,8 @@ function scanPublicSignature( fileInfos: ReadonlyMap, fileInfo: FileInfo, diagnostics: Array, - node: ts.Node + node: ts.Node, + workspacePackages: ReadonlyMap ) { if (ts.isFunctionDeclaration(node)) { scanFunctionLikeSignature(fileInfos, fileInfo, diagnostics, node) @@ -562,6 +607,21 @@ function scanPublicSignature( for (const member of node.members) { scanClassMemberSignature(fileInfos, fileInfo, diagnostics, member) } + } else if ( + ts.isExportDeclaration(node) && + node.exportClause !== undefined && + ts.isNamedExports(node.exportClause) + ) { + for (const specifier of node.exportClause.elements) { + for (const internal of getReExportedInternals(fileInfos, fileInfo, workspacePackages, specifier)) { + const name = specifier.propertyName ?? specifier.name + diagnostics.push({ + fileName: fileInfo.fileName, + range: [name.getStart(), name.getEnd()], + message: `Do not re-export @internal export "${internal.name}" from a public module` + }) + } + } } } @@ -579,7 +639,7 @@ function analyze(cwd: string): Analysis { } for (const fileInfo of fileInfos.values()) { - scanUsage(fileInfos, fileInfo) + scanUsage(fileInfos, fileInfo, workspacePackages) } const diagnosticsByFile = new Map>() @@ -587,7 +647,7 @@ function analyze(cwd: string): Analysis { for (const fileInfo of fileInfos.values()) { const diagnostics: Array = [] for (const declaration of fileInfo.publicDeclarations) { - scanPublicSignature(fileInfos, fileInfo, diagnostics, declaration) + scanPublicSignature(fileInfos, fileInfo, diagnostics, declaration, workspacePackages) } for (const diagnostic of diagnostics) { addFileDiagnostic(diagnosticsByFile, diagnostic) @@ -619,7 +679,7 @@ const rule: CreateRule = { meta: { type: "problem", docs: { - description: "Disallow unused @internal exports and references to @internal exports from public type signatures" + description: "Disallow unused @internal exports, public re-exports, and references from public type signatures" } }, create(context) { diff --git a/packages/tools/oxc/test/no-unused-internal.test.ts b/packages/tools/oxc/test/no-unused-internal.test.ts index a14ba07d6d4..1a1319d8b52 100644 --- a/packages/tools/oxc/test/no-unused-internal.test.ts +++ b/packages/tools/oxc/test/no-unused-internal.test.ts @@ -86,6 +86,23 @@ export interface Public { expect(run(cwd, "packages/foo/src/Internal.ts")).toEqual([]) }) + it("reports public re-exports of @internal exports", () => { + const cwd = writeFixture({ + "packages/foo/src/Internal.ts": `/** @internal */ +export const internal = 1 +`, + "packages/foo/src/Public.ts": `import { internal } from "./Internal.ts" + +export { internal } +` + }) + + expect(run(cwd, "packages/foo/src/Public.ts")).toEqual([ + `Do not re-export @internal export "internal" from a public module` + ]) + expect(run(cwd, "packages/foo/src/Internal.ts")).toEqual([]) + }) + it("ignores @internal class member type signatures", () => { const cwd = writeFixture({ "packages/foo/src/Internal.ts": `/** @internal */ From a54b8414b07857c896e953cc81c9d421d09ab56d Mon Sep 17 00:00:00 2001 From: Tim Smart Date: Mon, 20 Jul 2026 10:20:48 +1200 Subject: [PATCH 2/3] Fix public pre-response handler re-export --- .../effect/src/unstable/http/HttpEffect.ts | 24 +++++++++++-------- .../http/internal/preResponseHandler.ts | 1 + .../tools/oxc/test/no-unused-internal.test.ts | 4 +--- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/effect/src/unstable/http/HttpEffect.ts b/packages/effect/src/unstable/http/HttpEffect.ts index 27afb26fff9..02a149a95eb 100644 --- a/packages/effect/src/unstable/http/HttpEffect.ts +++ b/packages/effect/src/unstable/http/HttpEffect.ts @@ -25,7 +25,10 @@ import { HttpServerRequest } from "./HttpServerRequest.ts" import * as Request from "./HttpServerRequest.ts" import type { HttpServerResponse } from "./HttpServerResponse.ts" import * as Response from "./HttpServerResponse.ts" -import { appendPreResponseHandlerUnsafe, requestPreResponseHandlers } from "./internal/preResponseHandler.ts" +import { + appendPreResponseHandlerUnsafe as appendPreResponseHandlerUnsafeInternal, + requestPreResponseHandlers +} from "./internal/preResponseHandler.ts" /** * Runs an HTTP server effect, sends the produced response with the supplied handler, and converts failures into HTTP responses. @@ -192,15 +195,16 @@ export const appendPreResponseHandler = (handler: PreResponseHandler): Effect.Ef return Effect.void }) -export { - /** - * Registers a pre-response handler for the supplied HTTP server request. - * - * @category fiber refs - * @since 4.0.0 - */ - appendPreResponseHandlerUnsafe -} +/** + * Registers a pre-response handler for the supplied HTTP server request. + * + * @category fiber refs + * @since 4.0.0 + */ +export const appendPreResponseHandlerUnsafe: ( + request: HttpServerRequest, + handler: PreResponseHandler +) => void = appendPreResponseHandlerUnsafeInternal /** * Runs an effect after registering a pre-response handler for the current HTTP server request. diff --git a/packages/effect/src/unstable/http/internal/preResponseHandler.ts b/packages/effect/src/unstable/http/internal/preResponseHandler.ts index 07e781264f4..300c9c39a0c 100644 --- a/packages/effect/src/unstable/http/internal/preResponseHandler.ts +++ b/packages/effect/src/unstable/http/internal/preResponseHandler.ts @@ -5,6 +5,7 @@ import type { HttpServerRequest } from "../HttpServerRequest.ts" /** @internal */ export const requestPreResponseHandlers = new WeakMap() +/** @internal */ export const appendPreResponseHandlerUnsafe = (request: HttpServerRequest, handler: PreResponseHandler): void => { const prev = requestPreResponseHandlers.get(request.source) const next: PreResponseHandler = prev ? diff --git a/packages/tools/oxc/test/no-unused-internal.test.ts b/packages/tools/oxc/test/no-unused-internal.test.ts index 1a1319d8b52..2322ee8b02c 100644 --- a/packages/tools/oxc/test/no-unused-internal.test.ts +++ b/packages/tools/oxc/test/no-unused-internal.test.ts @@ -91,9 +91,7 @@ export interface Public { "packages/foo/src/Internal.ts": `/** @internal */ export const internal = 1 `, - "packages/foo/src/Public.ts": `import { internal } from "./Internal.ts" - -export { internal } + "packages/foo/src/Public.ts": `export { internal } from "./Internal.ts" ` }) From 9fab402afeada0f46821906a608fae2b10408010 Mon Sep 17 00:00:00 2001 From: Tim Smart Date: Mon, 20 Jul 2026 10:25:12 +1200 Subject: [PATCH 3/3] wip --- packages/effect/src/unstable/http/HttpEffect.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/effect/src/unstable/http/HttpEffect.ts b/packages/effect/src/unstable/http/HttpEffect.ts index 02a149a95eb..e4bad7d8f49 100644 --- a/packages/effect/src/unstable/http/HttpEffect.ts +++ b/packages/effect/src/unstable/http/HttpEffect.ts @@ -25,10 +25,7 @@ import { HttpServerRequest } from "./HttpServerRequest.ts" import * as Request from "./HttpServerRequest.ts" import type { HttpServerResponse } from "./HttpServerResponse.ts" import * as Response from "./HttpServerResponse.ts" -import { - appendPreResponseHandlerUnsafe as appendPreResponseHandlerUnsafeInternal, - requestPreResponseHandlers -} from "./internal/preResponseHandler.ts" +import * as preResponseHandler from "./internal/preResponseHandler.ts" /** * Runs an HTTP server effect, sends the produced response with the supplied handler, and converts failures into HTTP responses. @@ -49,7 +46,7 @@ export const toHandled = ( const fiber = Fiber.getCurrent()! reportCauseUnsafe(fiber, cause) const request = Context.getUnsafe(fiber.context, HttpServerRequest) - const handler = requestPreResponseHandlers.get(request.source) + const handler = preResponseHandler.requestPreResponseHandlers.get(request.source) const cont = cause.reasons.length === 0 ? Effect.succeed(response) : Effect.failCause(cause) if (handler === undefined) { ;(request as any)[handledSymbol] = true @@ -72,7 +69,7 @@ export const toHandled = ( onSuccess: (response) => { const fiber = Fiber.getCurrent()! const request = Context.getUnsafe(fiber.context, HttpServerRequest) - const handler = requestPreResponseHandlers.get(request.source) + const handler = preResponseHandler.requestPreResponseHandlers.get(request.source) if (handler === undefined) { ;(request as any)[handledSymbol] = true return Effect.mapEager(handleResponse(request, response), () => response) @@ -204,7 +201,7 @@ export const appendPreResponseHandler = (handler: PreResponseHandler): Effect.Ef export const appendPreResponseHandlerUnsafe: ( request: HttpServerRequest, handler: PreResponseHandler -) => void = appendPreResponseHandlerUnsafeInternal +) => void = preResponseHandler.appendPreResponseHandlerUnsafe /** * Runs an effect after registering a pre-response handler for the current HTTP server request.