diff --git a/package-lock.json b/package-lock.json index bd245ead7e8..bdb655ef533 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@netlify/build-info": "7.13.2", "@netlify/config": "20.12.1", "@netlify/edge-bundler": "11.3.0", + "@netlify/edge-functions": "2.5.1", "@netlify/local-functions-proxy": "1.1.1", "@netlify/zip-it-and-ship-it": "9.31.1", "@octokit/rest": "19.0.13", @@ -3765,6 +3766,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@netlify/edge-functions": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions/-/edge-functions-2.5.1.tgz", + "integrity": "sha512-6YGlbzxPaSqc/D2LhP4T4PXrim/vRmqpO1RwQKqVod6WCWlkdtJcAd3mGoI7efrjfND8twh7TqXtL7RRCI23qA==" + }, "node_modules/@netlify/eslint-config-node": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@netlify/eslint-config-node/-/eslint-config-node-7.0.0.tgz", @@ -26476,6 +26482,11 @@ } } }, + "@netlify/edge-functions": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@netlify/edge-functions/-/edge-functions-2.5.1.tgz", + "integrity": "sha512-6YGlbzxPaSqc/D2LhP4T4PXrim/vRmqpO1RwQKqVod6WCWlkdtJcAd3mGoI7efrjfND8twh7TqXtL7RRCI23qA==" + }, "@netlify/eslint-config-node": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@netlify/eslint-config-node/-/eslint-config-node-7.0.0.tgz", diff --git a/package.json b/package.json index aa2f42fa9ef..348138c6d56 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@netlify/build-info": "7.13.2", "@netlify/config": "20.12.1", "@netlify/edge-bundler": "11.3.0", + "@netlify/edge-functions": "2.5.1", "@netlify/local-functions-proxy": "1.1.1", "@netlify/zip-it-and-ship-it": "9.31.1", "@octokit/rest": "19.0.13", diff --git a/src/commands/deploy/deploy.ts b/src/commands/deploy/deploy.ts index 8af8bb2c677..ddb2583080e 100644 --- a/src/commands/deploy/deploy.ts +++ b/src/commands/deploy/deploy.ts @@ -597,7 +597,7 @@ const bundleEdgeFunctions = async (options, command: BaseCommand) => { packagePath: command.workspacePackage, buffer: true, featureFlags: edgeFunctionsFeatureFlags, - edgeFunctionsBootstrapURL: getBootstrapURL(), + edgeFunctionsBootstrapURL: await getBootstrapURL(), }) if (!success) { diff --git a/src/lib/build.ts b/src/lib/build.ts index 15258061df9..ade68bac9d7 100644 --- a/src/lib/build.ts +++ b/src/lib/build.ts @@ -30,7 +30,7 @@ import { featureFlags as edgeFunctionsFeatureFlags } from './edge-functions/cons * @param {*} config.deployHandler * @returns {BuildConfig} */ -export const getBuildOptions = ({ +export const getBuildOptions = async ({ // @ts-expect-error TS(7031) FIXME: Binding element 'cachedConfig' implicitly has an '... Remove this comment to see the full error message cachedConfig, // @ts-expect-error TS(7031) FIXME: Binding element 'currentDir' implicitly has an 'an... Remove this comment to see the full error message @@ -89,7 +89,7 @@ export const getBuildOptions = ({ functionsBundlingManifest: true, }, eventHandlers, - edgeFunctionsBootstrapURL: getBootstrapURL(), + edgeFunctionsBootstrapURL: await getBootstrapURL(), } } diff --git a/src/lib/edge-functions/bootstrap.ts b/src/lib/edge-functions/bootstrap.ts index b64991c0baa..219f30171df 100644 --- a/src/lib/edge-functions/bootstrap.ts +++ b/src/lib/edge-functions/bootstrap.ts @@ -1,5 +1,24 @@ import { env } from 'process' -const latestBootstrapURL = 'https://65f9b38dc160de0008d35515--edge.netlify.com/bootstrap/index-combined.ts' +import { getURL } from '@netlify/edge-functions/version' -export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL +import { warn } from '../../utils/command-helpers.js' + +export const FALLBACK_BOOTSTRAP_URL = 'https://edge.netlify.com/bootstrap/index-combined.ts' + +export const getBootstrapURL = async () => { + if (env.NETLIFY_EDGE_BOOTSTRAP) { + return env.NETLIFY_EDGE_BOOTSTRAP + } + + try { + return await getURL() + } catch (error) { + warn(`Could not load latest version of Edge Functions environment: ${(error as NodeJS.ErrnoException)?.message}`) + + // If there was an error getting the bootstrap URL from the module, let's + // use the latest version of the bootstrap. This is not ideal, but better + // than failing to serve requests with edge functions. + return FALLBACK_BOOTSTRAP_URL + } +} diff --git a/src/lib/edge-functions/proxy.ts b/src/lib/edge-functions/proxy.ts index 850da6ef87a..d0f70952a3d 100644 --- a/src/lib/edge-functions/proxy.ts +++ b/src/lib/edge-functions/proxy.ts @@ -227,7 +227,7 @@ const prepareServer = async ({ const runIsolate = await bundler.serve({ ...getDownloadUpdateFunctions(), basePath: projectDir, - bootstrapURL: getBootstrapURL(), + bootstrapURL: await getBootstrapURL(), debug, distImportMapPath: join(projectDir, distImportMapPath), featureFlags, diff --git a/src/utils/run-build.ts b/src/utils/run-build.ts index 8a118f94c7c..c55a6458fe8 100644 --- a/src/utils/run-build.ts +++ b/src/utils/run-build.ts @@ -84,7 +84,7 @@ export const runNetlifyBuild = async ({ cwd: cachedConfig.buildDir, quiet: options.quiet, saveConfig: options.saveConfig, - edgeFunctionsBootstrapURL: getBootstrapURL(), + edgeFunctionsBootstrapURL: await getBootstrapURL(), } const devCommand = async (settingsOverrides = {}) => { diff --git a/tests/unit/lib/edge-functions/bootstrap.test.js b/tests/unit/lib/edge-functions/bootstrap.test.js new file mode 100644 index 00000000000..bcb15920695 --- /dev/null +++ b/tests/unit/lib/edge-functions/bootstrap.test.js @@ -0,0 +1,32 @@ +import { env } from 'process' + +import { describe, expect, test } from 'vitest' + +import { getBootstrapURL, FALLBACK_BOOTSTRAP_URL } from '../../../../dist/lib/edge-functions/bootstrap.js' + +describe('`getBootstrapURL()`', () => { + test('Returns the URL in the `NETLIFY_EDGE_BOOTSTRAP` URL, if set', async () => { + const mockBootstrapURL = 'https://edge.netlify/bootstrap.ts' + + env.NETLIFY_EDGE_BOOTSTRAP = mockBootstrapURL + + const bootstrapURL = await getBootstrapURL() + + delete env.NETLIFY_EDGE_BOOTSTRAP + + expect(bootstrapURL).toEqual(mockBootstrapURL) + }) + + test('Returns a publicly accessible URL', async () => { + const bootstrapURL = await getBootstrapURL() + + // We shouldn't get the fallback URL, because that means we couldn't get + // the URL from the `@netlify/edge-functions` module. + expect(bootstrapURL).not.toBe(FALLBACK_BOOTSTRAP_URL) + + const res = await fetch(bootstrapURL) + + expect(res.status).toBe(200) + expect(res.headers.get('content-type').startsWith('application/typescript')).toBe(true) + }) +})