Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/api-logger-foreign-response.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'e2b': patch
---

Fix every API call failing on Node with "onResponse: must return new Response() when modifying the response" when a `logger` is passed. The API logging middleware no longer returns the unchanged request and response, which openapi-fetch rejected because undici's `Response` is not the global one.
8 changes: 5 additions & 3 deletions packages/js-sdk/src/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,22 @@ export function createRpcLogger(logger: Logger): Interceptor {
}
}

/**
* The hooks return nothing: openapi-fetch treats a returned value as a
* replacement and requires it to be an instance of the global `Request` /
* `Response`, which undici's responses on Node are not.
*/
export function createApiLogger(logger: Logger): Middleware {
return {
async onRequest({ request }) {
logger.info?.(`Request ${request.method} ${request.url}`)
return request
},
async onResponse({ response }) {
if (response.status >= 400) {
logger.error?.('Response:', response.status, response.statusText)
} else {
logger.info?.('Response:', response.status, response.statusText)
}

return response
},
}
}
38 changes: 37 additions & 1 deletion packages/js-sdk/tests/logs.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import createClient from 'openapi-fetch'
import { Response as UndiciResponse } from 'undici'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep undici out of the shared browser test

When the browser matrix job runs pnpm test:browser, tests/runtimes/browser/vitest.config.mts includes every tests/**/*.test.ts and excludes tests/undici.test.ts specifically because the undici package is Node-only. This new top-level import therefore makes Vite load undici in Chromium before the test can run, causing the required browser leg (and thus SDK Tests Status) to fail during module setup. Move this regression case into the already excluded Node-only suite or construct a foreign response without statically importing undici.

Useful? React with 👍 / 👎.

import { assert, describe, test } from 'vitest'
import { createRpcLogger } from '../src/logs'
import { createApiLogger, createRpcLogger } from '../src/logs'

const req = { url: 'http://localhost:49983/process.Process/Start' } as any

Expand Down Expand Up @@ -37,3 +39,37 @@ describe('createRpcLogger', () => {
])
})
})

describe('createApiLogger', () => {
// On Node the API client fetches through undici, whose Response is not the
// global one; openapi-fetch rejects any onResponse result that is not an
// instance of the global Response.
test('logs responses that are not instances of the global Response', async () => {
assert.notStrictEqual(UndiciResponse, globalThis.Response)
const logs: any[][] = []
const client = createClient<any>({
baseUrl: 'http://localhost',
fetch: async () =>
new UndiciResponse('{"message":"boom"}', {
status: 500,
statusText: 'Internal Server Error',
headers: { 'content-type': 'application/json' },
}) as unknown as Response,
})
client.use(
createApiLogger({
info: (...args) => logs.push(['info', ...args]),
error: (...args) => logs.push(['error', ...args]),
})
)

const res = await client.GET('/sandboxes' as any)

assert.equal(res.response.status, 500)
assert.deepEqual(res.error, { message: 'boom' })
assert.deepEqual(logs, [
['info', 'Request GET http://localhost/sandboxes'],
['error', 'Response:', 500, 'Internal Server Error'],
])
})
})