diff --git a/workspaces/lightspeed/.changeset/blue-donuts-wash.md b/workspaces/lightspeed/.changeset/blue-donuts-wash.md new file mode 100644 index 0000000000..9e27029366 --- /dev/null +++ b/workspaces/lightspeed/.changeset/blue-donuts-wash.md @@ -0,0 +1,59 @@ +--- +'@red-hat-developer-hub/backstage-plugin-lightspeed-backend': minor +--- + +**BREAKING** Replaces `fetch` function with built-in one and refactors source to fit the change. This change comes from [ADR014](https://github.com/backstage/backstage/blob/master/docs/architecture-decisions/adr014-use-fetch.md) that now recommends the use of the global built-in `fetch` function since Node v20. + +The changes are contained for the `lightspeed-backend` plugin, the `node-fetch` direct dependency is removed from `package.json` and makes the following changes to the `router.ts` source: + +```diff +import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter'; +import { NotAllowedError } from '@backstage/errors'; +import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; + +import express, { Router } from 'express'; +import { createProxyMiddleware } from 'http-proxy-middleware'; +-import fetch from 'node-fetch'; + +import { + lightspeedChatCreatePermission, + lightspeedChatDeletePermission, + lightspeedChatReadPermission, + lightspeedPermissions, +} from '@red-hat-developer-hub/backstage-plugin-lightspeed-common'; + ++import { Readable } from 'node:stream'; ++ +import { userPermissionAuthorization } from './permission'; +import { + DEFAULT_HISTORY_LENGTH, + QueryRequestBody, + RouterOptions, +} from './types'; +import { validateCompletionsRequest } from './validation'; +``` + +Response piping has changed for the result of the built-in `fetch`: + +```diff +if (!fetchResponse.ok) { + // Read the error body + const errorBody = await fetchResponse.json(); + const errormsg = `Error from lightspeed-core server: ${errorBody.error?.message || errorBody?.detail?.cause || 'Unknown error'}`; + logger.error(errormsg); + + // Return a 500 status for any upstream error + response.status(500).json({ + error: errormsg, + }); ++ ++ return +} + +// Pipe the response back to the original response +-fetchResponse.body.pipe(response); ++if (fetchResponse.body) { ++ const nodeStream = Readable.fromWeb(fetchResponse.body as any); ++ nodeStream.pipe(response); ++} +``` diff --git a/workspaces/lightspeed/.yarnrc.yml b/workspaces/lightspeed/.yarnrc.yml new file mode 100644 index 0000000000..ac9dcf98c7 --- /dev/null +++ b/workspaces/lightspeed/.yarnrc.yml @@ -0,0 +1 @@ +yarnPath: ../../.yarn/releases/yarn-4.12.0.cjs diff --git a/workspaces/lightspeed/package.json b/workspaces/lightspeed/package.json index e2447a941f..608e24cb7d 100644 --- a/workspaces/lightspeed/package.json +++ b/workspaces/lightspeed/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "engines": { - "node": "18 || 20" + "node": "22" }, "scripts": { "dev": "backstage-cli repo start", diff --git a/workspaces/lightspeed/plugins/lightspeed-backend/package.json b/workspaces/lightspeed/plugins/lightspeed-backend/package.json index 95c6633f34..3ce51d35fc 100644 --- a/workspaces/lightspeed/plugins/lightspeed-backend/package.json +++ b/workspaces/lightspeed/plugins/lightspeed-backend/package.json @@ -51,8 +51,7 @@ "@langchain/openai": "^0.6.0", "@red-hat-developer-hub/backstage-plugin-lightspeed-common": "workspace:^", "express": "^4.21.1", - "http-proxy-middleware": "^3.0.2", - "node-fetch": "2.7.0" + "http-proxy-middleware": "^3.0.2" }, "devDependencies": { "@backstage/backend-test-utils": "^1.10.4", diff --git a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts index c363296eb1..6eb8208115 100644 --- a/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts +++ b/workspaces/lightspeed/plugins/lightspeed-backend/src/service/router.ts @@ -20,7 +20,6 @@ import { createPermissionIntegrationRouter } from '@backstage/plugin-permission- import express, { Router } from 'express'; import { createProxyMiddleware } from 'http-proxy-middleware'; -import fetch from 'node-fetch'; import { lightspeedChatCreatePermission, @@ -29,6 +28,8 @@ import { lightspeedPermissions, } from '@red-hat-developer-hub/backstage-plugin-lightspeed-common'; +import { Readable } from 'node:stream'; + import { userPermissionAuthorization } from './permission'; import { DEFAULT_HISTORY_LENGTH, @@ -241,10 +242,15 @@ export async function createRouter( response.status(500).json({ error: errormsg, }); + + return; } // Pipe the response back to the original response - fetchResponse.body.pipe(response); + if (fetchResponse.body) { + const nodeStream = Readable.fromWeb(fetchResponse.body as any); + nodeStream.pipe(response); + } } catch (error) { const errormsg = `Error fetching completions from ${provider}: ${error}`; logger.error(errormsg); diff --git a/workspaces/lightspeed/yarn.lock b/workspaces/lightspeed/yarn.lock index 0a6cc4b54a..3d32a262de 100644 --- a/workspaces/lightspeed/yarn.lock +++ b/workspaces/lightspeed/yarn.lock @@ -10368,7 +10368,6 @@ __metadata: express: "npm:^4.21.1" http-proxy-middleware: "npm:^3.0.2" msw: "npm:2.12.10" - node-fetch: "npm:2.7.0" prettier: "npm:3.8.1" supertest: "npm:6.3.4" languageName: unknown @@ -18606,11 +18605,11 @@ __metadata: linkType: hard "error-ex@npm:^1.3.1": - version: 1.3.2 - resolution: "error-ex@npm:1.3.2" + version: 1.3.4 + resolution: "error-ex@npm:1.3.4" dependencies: is-arrayish: "npm:^0.2.1" - checksum: 10c0/ba827f89369b4c93382cfca5a264d059dfefdaa56ecc5e338ffa58a6471f5ed93b71a20add1d52290a4873d92381174382658c885ac1a2305f7baca363ce9cce + checksum: 10c0/b9e34ff4778b8f3b31a8377e1c654456f4c41aeaa3d10a1138c3b7635d8b7b2e03eb2475d46d8ae055c1f180a1063e100bffabf64ea7e7388b37735df5328664 languageName: node linkType: hard @@ -22103,7 +22102,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.0": +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.16.1": version: 2.16.1 resolution: "is-core-module@npm:2.16.1" dependencies: @@ -22424,11 +22423,11 @@ __metadata: linkType: hard "is-ssh@npm:^1.4.0": - version: 1.4.0 - resolution: "is-ssh@npm:1.4.0" + version: 1.4.1 + resolution: "is-ssh@npm:1.4.1" dependencies: protocols: "npm:^2.0.1" - checksum: 10c0/3eb30d1bcb4507cd25562e7ac61a1c0aa31772134c67cec9c3afe6f4d57ec17e8c2892600a608e8e583f32f53f36465b8968c0305f2855cfbff95acfd049e113 + checksum: 10c0/021a7355cb032625d58db3cc8266ad9aa698cbabf460b71376a0307405577fd7d3aa0826c0bf1951d7809f134c0ee80403306f6d7633db94a5a3600a0106b398 languageName: node linkType: hard @@ -26266,7 +26265,7 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:2.7.0, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.9, node-fetch@npm:^2.7.0": +"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.12, node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.9, node-fetch@npm:^2.7.0": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -30065,15 +30064,15 @@ __metadata: linkType: hard "resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:~1.22.1, resolve@npm:~1.22.2": - version: 1.22.10 - resolution: "resolve@npm:1.22.10" + version: 1.22.11 + resolution: "resolve@npm:1.22.11" dependencies: - is-core-module: "npm:^2.16.0" + is-core-module: "npm:^2.16.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10c0/8967e1f4e2cc40f79b7e080b4582b9a8c5ee36ffb46041dccb20e6461161adf69f843b43067b4a375de926a2cd669157e29a29578191def399dd5ef89a1b5203 + checksum: 10c0/f657191507530f2cbecb5815b1ee99b20741ea6ee02a59c57028e9ec4c2c8d7681afcc35febbd554ac0ded459db6f2d8153382c53a2f266cee2575e512674409 languageName: node linkType: hard @@ -30104,15 +30103,15 @@ __metadata: linkType: hard "resolve@patch:resolve@npm%3A^1.17.0#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin, resolve@patch:resolve@npm%3A~1.22.2#optional!builtin": - version: 1.22.10 - resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin::version=1.22.10&hash=c3c19d" + version: 1.22.11 + resolution: "resolve@patch:resolve@npm%3A1.22.11#optional!builtin::version=1.22.11&hash=c3c19d" dependencies: - is-core-module: "npm:^2.16.0" + is-core-module: "npm:^2.16.1" path-parse: "npm:^1.0.7" supports-preserve-symlinks-flag: "npm:^1.0.0" bin: resolve: bin/resolve - checksum: 10c0/52a4e505bbfc7925ac8f4cd91fd8c4e096b6a89728b9f46861d3b405ac9a1ccf4dcbf8befb4e89a2e11370dacd0160918163885cbc669369590f2f31f4c58939 + checksum: 10c0/ee5b182f2e37cb1165465e58c6abc797fec0a80b5ba3231607beb4677db0c9291ac010c47cf092b6daa2b7f518d69a0e21888e7e2b633f68d501a874212a8c63 languageName: node linkType: hard