netlify-cli 17.1.0 → 17.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/npm-shrinkwrap.json +2369 -1011
- package/package.json +4 -3
- package/src/commands/dev/dev.mjs +1 -0
- package/src/commands/functions/functions-serve.mjs +1 -1
- package/src/commands/lm/lm-info.mjs +1 -1
- package/src/commands/lm/lm-setup.mjs +1 -1
- package/src/lib/edge-functions/proxy.mjs +7 -17
- package/src/lib/edge-functions/registry.mjs +179 -110
- package/src/lib/images/proxy.mjs +115 -0
- package/src/utils/lm/install.mjs +1 -1
- package/src/utils/proxy-server.mjs +3 -0
- package/src/utils/proxy.mjs +43 -6
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "17.
|
|
4
|
+
"version": "17.2.0",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@netlify/build": "29.25.0",
|
|
49
49
|
"@netlify/build-info": "7.10.2",
|
|
50
50
|
"@netlify/config": "20.9.0",
|
|
51
|
-
"@netlify/edge-bundler": "
|
|
51
|
+
"@netlify/edge-bundler": "^10.1.0",
|
|
52
52
|
"@netlify/local-functions-proxy": "1.1.1",
|
|
53
53
|
"@netlify/zip-it-and-ship-it": "9.25.7",
|
|
54
54
|
"@octokit/rest": "19.0.13",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"https-proxy-agent": "5.0.1",
|
|
101
101
|
"inquirer": "6.5.2",
|
|
102
102
|
"inquirer-autocomplete-prompt": "1.4.0",
|
|
103
|
+
"ipx": "^2.0.1",
|
|
103
104
|
"is-docker": "3.0.0",
|
|
104
105
|
"is-stream": "3.0.0",
|
|
105
106
|
"is-wsl": "2.2.0",
|
|
@@ -108,7 +109,7 @@
|
|
|
108
109
|
"jsonwebtoken": "9.0.1",
|
|
109
110
|
"jwt-decode": "3.1.2",
|
|
110
111
|
"lambda-local": "2.1.2",
|
|
111
|
-
"
|
|
112
|
+
"listr2": "^7.0.2",
|
|
112
113
|
"locate-path": "7.2.0",
|
|
113
114
|
"lodash": "4.17.21",
|
|
114
115
|
"log-symbols": "5.1.0",
|
package/src/commands/dev/dev.mjs
CHANGED
|
@@ -71,7 +71,7 @@ export const createFunctionsServeCommand = (program) =>
|
|
|
71
71
|
program
|
|
72
72
|
.command('functions:serve')
|
|
73
73
|
.alias('function:serve')
|
|
74
|
-
.description('
|
|
74
|
+
.description('Serve functions locally')
|
|
75
75
|
.option('-f, --functions <dir>', 'Specify a functions directory to serve')
|
|
76
76
|
.option('-p, --port <port>', 'Specify a port for the functions server', (value) => Number.parseInt(value))
|
|
77
77
|
.option('-o, --offline', 'disables any features that require network access')
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { Buffer } from 'buffer'
|
|
3
3
|
import { rm } from 'fs/promises'
|
|
4
|
-
import { join,
|
|
4
|
+
import { join, resolve } from 'path'
|
|
5
5
|
|
|
6
6
|
// eslint-disable-next-line import/no-namespace
|
|
7
7
|
import * as bundler from '@netlify/edge-bundler'
|
|
8
8
|
import getAvailablePort from 'get-port'
|
|
9
9
|
|
|
10
|
-
import { NETLIFYDEVERR,
|
|
10
|
+
import { NETLIFYDEVERR, chalk, error as printError } from '../../utils/command-helpers.mjs'
|
|
11
11
|
import { getGeoLocation } from '../geo-location.mjs'
|
|
12
12
|
import { getPathInProject } from '../settings.mjs'
|
|
13
13
|
import { startSpinner, stopSpinner } from '../spinner.mjs'
|
|
@@ -99,6 +99,7 @@ export const initializeProxy = async ({
|
|
|
99
99
|
offline,
|
|
100
100
|
passthroughPort,
|
|
101
101
|
projectDir,
|
|
102
|
+
repositoryRoot,
|
|
102
103
|
settings,
|
|
103
104
|
siteInfo,
|
|
104
105
|
state,
|
|
@@ -132,6 +133,7 @@ export const initializeProxy = async ({
|
|
|
132
133
|
internalFunctions,
|
|
133
134
|
port: isolatePort,
|
|
134
135
|
projectDir,
|
|
136
|
+
repositoryRoot,
|
|
135
137
|
})
|
|
136
138
|
const hasEdgeFunctions = userFunctionsPath !== undefined || internalFunctionsPath
|
|
137
139
|
|
|
@@ -162,21 +164,7 @@ export const initializeProxy = async ({
|
|
|
162
164
|
await registry.initialize()
|
|
163
165
|
|
|
164
166
|
const url = new URL(req.url, `http://${LOCAL_HOST}:${mainPort}`)
|
|
165
|
-
const { functionNames, invocationMetadata
|
|
166
|
-
|
|
167
|
-
// If the request matches a config declaration for an Edge Function without
|
|
168
|
-
// a matching function file, we warn the user.
|
|
169
|
-
orphanedDeclarations.forEach((functionName) => {
|
|
170
|
-
log(
|
|
171
|
-
`${NETLIFYDEVWARN} Request to ${chalk.yellow(
|
|
172
|
-
url.pathname,
|
|
173
|
-
)} matches declaration for edge function ${chalk.yellow(
|
|
174
|
-
functionName,
|
|
175
|
-
)}, but there's no matching function file in ${chalk.yellow(
|
|
176
|
-
relative(projectDir, userFunctionsPath),
|
|
177
|
-
)}. Please visit ${chalk.blue('https://ntl.fyi/edge-create')} for more information.`,
|
|
178
|
-
)
|
|
179
|
-
})
|
|
167
|
+
const { functionNames, invocationMetadata } = registry.matchURLPath(url.pathname, req.method)
|
|
180
168
|
|
|
181
169
|
if (functionNames.length === 0) {
|
|
182
170
|
return
|
|
@@ -217,6 +205,7 @@ const prepareServer = async ({
|
|
|
217
205
|
internalFunctions,
|
|
218
206
|
port,
|
|
219
207
|
projectDir,
|
|
208
|
+
repositoryRoot,
|
|
220
209
|
}) => {
|
|
221
210
|
// Merging internal with user-defined import maps.
|
|
222
211
|
const importMapPaths = [...importMaps, config.functions['*'].deno_import_map]
|
|
@@ -243,6 +232,7 @@ const prepareServer = async ({
|
|
|
243
232
|
importMapPaths,
|
|
244
233
|
inspectSettings,
|
|
245
234
|
port,
|
|
235
|
+
rootPath: repositoryRoot,
|
|
246
236
|
servePath,
|
|
247
237
|
})
|
|
248
238
|
const registry = new EdgeFunctionsRegistry({
|
|
@@ -13,8 +13,14 @@ import {
|
|
|
13
13
|
|
|
14
14
|
/** @typedef {import('@netlify/edge-bundler').Declaration} Declaration */
|
|
15
15
|
/** @typedef {import('@netlify/edge-bundler').EdgeFunction} EdgeFunction */
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {"buildError" | "loaded" | "reloaded" | "reloading" | "removed"} EdgeFunctionEvent
|
|
18
|
+
*/
|
|
16
19
|
/** @typedef {import('@netlify/edge-bundler').FunctionConfig} FunctionConfig */
|
|
20
|
+
/** @typedef {import('@netlify/edge-bundler').Manifest} Manifest */
|
|
21
|
+
/** @typedef {import('@netlify/edge-bundler').ModuleGraph} ModuleGraph */
|
|
17
22
|
/** @typedef {Awaited<ReturnType<typeof import('@netlify/edge-bundler').serve>>} RunIsolate */
|
|
23
|
+
/** @typedef {Omit<Manifest["routes"][0], "pattern"> & { pattern: RegExp }} Route */
|
|
18
24
|
|
|
19
25
|
const featureFlags = { edge_functions_correct_order: true }
|
|
20
26
|
|
|
@@ -46,12 +52,6 @@ export class EdgeFunctionsRegistry {
|
|
|
46
52
|
/** @type {Declaration[]} */
|
|
47
53
|
#declarationsFromDeployConfig
|
|
48
54
|
|
|
49
|
-
/** @type {Record<string, FunctionConfig>} */
|
|
50
|
-
#userFunctionConfigs = {}
|
|
51
|
-
|
|
52
|
-
/** @type {Record<string, FunctionConfig>} */
|
|
53
|
-
#internalFunctionConfigs = {}
|
|
54
|
-
|
|
55
55
|
/** @type {Declaration[]} */
|
|
56
56
|
#declarationsFromTOML
|
|
57
57
|
|
|
@@ -67,6 +67,9 @@ export class EdgeFunctionsRegistry {
|
|
|
67
67
|
/** @type {Map<string, string>} */
|
|
68
68
|
#functionPaths = new Map()
|
|
69
69
|
|
|
70
|
+
/** @type {Manifest | null} */
|
|
71
|
+
#manifest = null
|
|
72
|
+
|
|
70
73
|
/** @type {EdgeFunction[]} */
|
|
71
74
|
#userFunctions = []
|
|
72
75
|
|
|
@@ -76,6 +79,9 @@ export class EdgeFunctionsRegistry {
|
|
|
76
79
|
/** @type {Promise<void>} */
|
|
77
80
|
#initialScan
|
|
78
81
|
|
|
82
|
+
/** @type {Route[]} */
|
|
83
|
+
#routes = []
|
|
84
|
+
|
|
79
85
|
/** @type {string} */
|
|
80
86
|
#servePath
|
|
81
87
|
|
|
@@ -122,8 +128,6 @@ export class EdgeFunctionsRegistry {
|
|
|
122
128
|
this.#env = EdgeFunctionsRegistry.#getEnvironmentVariables(env)
|
|
123
129
|
|
|
124
130
|
this.#buildError = null
|
|
125
|
-
this.#userFunctionConfigs = {}
|
|
126
|
-
this.#internalFunctionConfigs = {}
|
|
127
131
|
this.#directoryWatchers = new Map()
|
|
128
132
|
this.#dependencyPaths = new Map()
|
|
129
133
|
this.#functionPaths = new Map()
|
|
@@ -141,12 +145,12 @@ export class EdgeFunctionsRegistry {
|
|
|
141
145
|
async #doInitialScan() {
|
|
142
146
|
await this.#scanForFunctions()
|
|
143
147
|
|
|
144
|
-
this.#functions.forEach((func) => {
|
|
145
|
-
this.#logAddedFunction(func)
|
|
146
|
-
})
|
|
147
|
-
|
|
148
148
|
try {
|
|
149
|
-
await this.#build()
|
|
149
|
+
const { warnings } = await this.#build()
|
|
150
|
+
|
|
151
|
+
this.#functions.forEach((func) => {
|
|
152
|
+
this.#logEvent('loaded', { functionName: func.name, warnings: warnings[func.name] })
|
|
153
|
+
})
|
|
150
154
|
} catch {
|
|
151
155
|
// no-op
|
|
152
156
|
}
|
|
@@ -160,17 +164,16 @@ export class EdgeFunctionsRegistry {
|
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
/**
|
|
163
|
-
* @return {Promise<
|
|
167
|
+
* @return {Promise<{warnings: Record<string, string[]>}>}
|
|
164
168
|
*/
|
|
165
169
|
async #build() {
|
|
170
|
+
/**
|
|
171
|
+
* @type Record<string, string[]>
|
|
172
|
+
*/
|
|
173
|
+
const warnings = {}
|
|
174
|
+
|
|
166
175
|
try {
|
|
167
|
-
const { functionsConfig, graph, npmSpecifiersWithExtraneousFiles, success } = await this.#
|
|
168
|
-
this.#functions,
|
|
169
|
-
this.#env,
|
|
170
|
-
{
|
|
171
|
-
getFunctionsConfig: true,
|
|
172
|
-
},
|
|
173
|
-
)
|
|
176
|
+
const { functionsConfig, graph, npmSpecifiersWithExtraneousFiles, success } = await this.#runBuild()
|
|
174
177
|
|
|
175
178
|
if (!success) {
|
|
176
179
|
throw new Error('Build error')
|
|
@@ -182,18 +185,39 @@ export class EdgeFunctionsRegistry {
|
|
|
182
185
|
// functionsConfig therefore contains first all internal functionConfigs and then user functionConfigs
|
|
183
186
|
let index = 0
|
|
184
187
|
|
|
185
|
-
|
|
188
|
+
/** @type {Record<string, FunctionConfig>} */
|
|
189
|
+
const internalFunctionConfigs = this.#internalFunctions.reduce(
|
|
186
190
|
// eslint-disable-next-line no-plusplus
|
|
187
191
|
(acc, func) => ({ ...acc, [func.name]: functionsConfig[index++] }),
|
|
188
192
|
{},
|
|
189
193
|
)
|
|
190
194
|
|
|
191
|
-
|
|
195
|
+
/** @type {Record<string, FunctionConfig>} */
|
|
196
|
+
const userFunctionConfigs = this.#userFunctions.reduce(
|
|
192
197
|
// eslint-disable-next-line no-plusplus
|
|
193
198
|
(acc, func) => ({ ...acc, [func.name]: functionsConfig[index++] }),
|
|
194
199
|
{},
|
|
195
200
|
)
|
|
196
201
|
|
|
202
|
+
const { manifest, routes, unroutedFunctions } = this.#buildRoutes(internalFunctionConfigs, userFunctionConfigs)
|
|
203
|
+
|
|
204
|
+
this.#manifest = manifest
|
|
205
|
+
this.#routes = routes
|
|
206
|
+
|
|
207
|
+
unroutedFunctions.forEach((name) => {
|
|
208
|
+
warnings[name] = warnings[name] || []
|
|
209
|
+
warnings[name].push(
|
|
210
|
+
`Edge function is not accessible because it does not have a path configured. Learn more at https://ntl.fyi/edge-create.`,
|
|
211
|
+
)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
for (const functionName in userFunctionConfigs) {
|
|
215
|
+
if ('paths' in userFunctionConfigs[functionName]) {
|
|
216
|
+
warnings[functionName] = warnings[functionName] || []
|
|
217
|
+
warnings[functionName].push(`Unknown 'paths' configuration property. Did you mean 'path'?`)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
197
221
|
this.#processGraph(graph)
|
|
198
222
|
|
|
199
223
|
if (npmSpecifiersWithExtraneousFiles.length !== 0) {
|
|
@@ -203,6 +227,8 @@ export class EdgeFunctionsRegistry {
|
|
|
203
227
|
`${NETLIFYDEVWARN} The following npm modules, which are directly or indirectly imported by an edge function, may not be supported: ${modules}. For more information, visit https://ntl.fyi/edge-functions-npm.`,
|
|
204
228
|
)
|
|
205
229
|
}
|
|
230
|
+
|
|
231
|
+
return { warnings }
|
|
206
232
|
} catch (error) {
|
|
207
233
|
this.#buildError = error
|
|
208
234
|
|
|
@@ -210,6 +236,38 @@ export class EdgeFunctionsRegistry {
|
|
|
210
236
|
}
|
|
211
237
|
}
|
|
212
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Builds a manifest and corresponding routes for the functions in the
|
|
241
|
+
* registry, taking into account the declarations from the TOML, from
|
|
242
|
+
* the deploy configuration API, and from the in-source configuration
|
|
243
|
+
* found in both internal and user functions.
|
|
244
|
+
*
|
|
245
|
+
* @param {Record<string, FunctionConfig>} internalFunctionConfigs
|
|
246
|
+
* @param {Record<string, FunctionConfig>} userFunctionConfigs
|
|
247
|
+
*/
|
|
248
|
+
#buildRoutes(internalFunctionConfigs, userFunctionConfigs) {
|
|
249
|
+
const declarations = this.#bundler.mergeDeclarations(
|
|
250
|
+
this.#declarationsFromTOML,
|
|
251
|
+
userFunctionConfigs,
|
|
252
|
+
internalFunctionConfigs,
|
|
253
|
+
this.#declarationsFromDeployConfig,
|
|
254
|
+
featureFlags,
|
|
255
|
+
)
|
|
256
|
+
const { declarationsWithoutFunction, manifest, unroutedFunctions } = this.#bundler.generateManifest({
|
|
257
|
+
declarations,
|
|
258
|
+
userFunctionConfig: userFunctionConfigs,
|
|
259
|
+
internalFunctionConfig: internalFunctionConfigs,
|
|
260
|
+
functions: this.#functions,
|
|
261
|
+
featureFlags,
|
|
262
|
+
})
|
|
263
|
+
const routes = [...manifest.routes, ...manifest.post_cache_routes].map((route) => ({
|
|
264
|
+
...route,
|
|
265
|
+
pattern: new RegExp(route.pattern),
|
|
266
|
+
}))
|
|
267
|
+
|
|
268
|
+
return { declarationsWithoutFunction, manifest, routes, unroutedFunctions }
|
|
269
|
+
}
|
|
270
|
+
|
|
213
271
|
/**
|
|
214
272
|
* @returns {Promise<void>}
|
|
215
273
|
*/
|
|
@@ -221,14 +279,14 @@ export class EdgeFunctionsRegistry {
|
|
|
221
279
|
}
|
|
222
280
|
|
|
223
281
|
try {
|
|
224
|
-
await this.#build()
|
|
282
|
+
const { warnings } = await this.#build()
|
|
225
283
|
|
|
226
284
|
deletedFunctions.forEach((func) => {
|
|
227
|
-
this.#
|
|
285
|
+
this.#logEvent('removed', { functionName: func.name, warnings: warnings[func.name] })
|
|
228
286
|
})
|
|
229
287
|
|
|
230
288
|
newFunctions.forEach((func) => {
|
|
231
|
-
this.#
|
|
289
|
+
this.#logEvent('loaded', { functionName: func.name, warnings: warnings[func.name] })
|
|
232
290
|
})
|
|
233
291
|
} catch {
|
|
234
292
|
// no-op
|
|
@@ -288,28 +346,21 @@ export class EdgeFunctionsRegistry {
|
|
|
288
346
|
return
|
|
289
347
|
}
|
|
290
348
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
log(`${NETLIFYDEVLOG} ${chalk.magenta('Reloading')} edge functions${reason}...`)
|
|
349
|
+
this.#logEvent('reloading', {})
|
|
294
350
|
|
|
295
351
|
try {
|
|
296
|
-
await this.#build()
|
|
297
|
-
|
|
352
|
+
const { warnings } = await this.#build()
|
|
298
353
|
const functionNames = [...matchingFunctions]
|
|
299
354
|
|
|
300
355
|
if (functionNames.length === 0) {
|
|
301
|
-
|
|
356
|
+
this.#logEvent('reloaded', {})
|
|
302
357
|
} else {
|
|
303
358
|
functionNames.forEach((functionName) => {
|
|
304
|
-
|
|
305
|
-
`${NETLIFYDEVLOG} ${chalk.green('Reloaded')} edge function ${chalk.yellow(
|
|
306
|
-
this.#getDisplayName(functionName),
|
|
307
|
-
)}`,
|
|
308
|
-
)
|
|
359
|
+
this.#logEvent('reloaded', { functionName, warnings: warnings[functionName] })
|
|
309
360
|
})
|
|
310
361
|
}
|
|
311
|
-
} catch {
|
|
312
|
-
|
|
362
|
+
} catch (error) {
|
|
363
|
+
this.#logEvent('buildError', { buildError: error?.message })
|
|
313
364
|
}
|
|
314
365
|
}
|
|
315
366
|
|
|
@@ -321,50 +372,72 @@ export class EdgeFunctionsRegistry {
|
|
|
321
372
|
}
|
|
322
373
|
|
|
323
374
|
/**
|
|
324
|
-
*
|
|
375
|
+
* Logs an event associated with functions.
|
|
376
|
+
*
|
|
377
|
+
* @param {EdgeFunctionEvent} event
|
|
378
|
+
* @param {object} data
|
|
379
|
+
* @param {Error} [data.buildError]
|
|
380
|
+
* @param {string} [data.functionName]
|
|
381
|
+
* @param {string[]} [data.warnings]
|
|
382
|
+
* @returns
|
|
325
383
|
*/
|
|
326
|
-
#
|
|
327
|
-
|
|
328
|
-
|
|
384
|
+
#logEvent(event, { buildError, functionName, warnings = [] }) {
|
|
385
|
+
const subject = functionName
|
|
386
|
+
? `edge function ${chalk.yellow(this.#getDisplayName(functionName))}`
|
|
387
|
+
: 'edge functions'
|
|
388
|
+
const warningsText =
|
|
389
|
+
warnings.length === 0 ? '' : ` with warnings:\n${warnings.map((warning) => ` - ${warning}`).join('\n')}`
|
|
329
390
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
391
|
+
if (event === 'buildError') {
|
|
392
|
+
log(`${NETLIFYDEVERR} ${chalk.red('Failed to load')} ${subject}: ${buildError}`)
|
|
393
|
+
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (event === 'loaded') {
|
|
398
|
+
const icon = warningsText ? NETLIFYDEVWARN : NETLIFYDEVLOG
|
|
399
|
+
const color = warningsText ? chalk.yellow : chalk.green
|
|
400
|
+
|
|
401
|
+
log(`${icon} ${color('Loaded')} ${subject}${warningsText}`)
|
|
402
|
+
|
|
403
|
+
return
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (event === 'reloaded') {
|
|
407
|
+
const icon = warningsText ? NETLIFYDEVWARN : NETLIFYDEVLOG
|
|
408
|
+
const color = warningsText ? chalk.yellow : chalk.green
|
|
409
|
+
|
|
410
|
+
log(`${icon} ${color('Reloaded')} ${subject}${warningsText}`)
|
|
411
|
+
|
|
412
|
+
return
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (event === 'reloading') {
|
|
416
|
+
log(`${NETLIFYDEVLOG} ${chalk.magenta('Reloading')} ${subject}...`)
|
|
417
|
+
|
|
418
|
+
return
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (event === 'removed') {
|
|
422
|
+
log(`${NETLIFYDEVLOG} ${chalk.magenta('Removed')} ${subject}`)
|
|
423
|
+
}
|
|
335
424
|
}
|
|
336
425
|
|
|
337
426
|
/**
|
|
427
|
+
* Returns the functions in the registry that should run for a given URL path
|
|
428
|
+
* and HTTP method, based on the routes registered for each function.
|
|
429
|
+
*
|
|
338
430
|
* @param {string} urlPath
|
|
339
431
|
* @param {string} method
|
|
340
432
|
*/
|
|
341
433
|
matchURLPath(urlPath, method) {
|
|
342
|
-
const declarations = this.#bundler.mergeDeclarations(
|
|
343
|
-
this.#declarationsFromTOML,
|
|
344
|
-
this.#userFunctionConfigs,
|
|
345
|
-
this.#internalFunctionConfigs,
|
|
346
|
-
this.#declarationsFromDeployConfig,
|
|
347
|
-
featureFlags,
|
|
348
|
-
)
|
|
349
|
-
const manifest = this.#bundler.generateManifest({
|
|
350
|
-
declarations,
|
|
351
|
-
userFunctionConfig: this.#userFunctionConfigs,
|
|
352
|
-
internalFunctionConfig: this.#internalFunctionConfigs,
|
|
353
|
-
functions: this.#functions,
|
|
354
|
-
featureFlags,
|
|
355
|
-
})
|
|
356
|
-
const routes = [...manifest.routes, ...manifest.post_cache_routes].map((route) => ({
|
|
357
|
-
...route,
|
|
358
|
-
pattern: new RegExp(route.pattern),
|
|
359
|
-
}))
|
|
360
|
-
|
|
361
434
|
/** @type string[] */
|
|
362
435
|
const functionNames = []
|
|
363
436
|
|
|
364
437
|
/** @type number[] */
|
|
365
438
|
const routeIndexes = []
|
|
366
439
|
|
|
367
|
-
routes.forEach((route, index) => {
|
|
440
|
+
this.#routes.forEach((route, index) => {
|
|
368
441
|
if (route.methods && route.methods.length !== 0 && !route.methods.includes(method)) {
|
|
369
442
|
return
|
|
370
443
|
}
|
|
@@ -373,7 +446,7 @@ export class EdgeFunctionsRegistry {
|
|
|
373
446
|
return
|
|
374
447
|
}
|
|
375
448
|
|
|
376
|
-
const isExcluded = manifest
|
|
449
|
+
const isExcluded = this.#manifest?.function_config[route.function]?.excluded_patterns?.some((pattern) =>
|
|
377
450
|
new RegExp(pattern).test(urlPath),
|
|
378
451
|
)
|
|
379
452
|
|
|
@@ -385,55 +458,24 @@ export class EdgeFunctionsRegistry {
|
|
|
385
458
|
routeIndexes.push(index)
|
|
386
459
|
})
|
|
387
460
|
const invocationMetadata = {
|
|
388
|
-
function_config: manifest
|
|
461
|
+
function_config: this.#manifest?.function_config,
|
|
389
462
|
req_routes: routeIndexes,
|
|
390
|
-
routes: manifest
|
|
463
|
+
routes: this.#manifest?.routes.map((route) => ({
|
|
464
|
+
function: route.function,
|
|
465
|
+
path: route.path,
|
|
466
|
+
pattern: route.pattern,
|
|
467
|
+
})),
|
|
391
468
|
}
|
|
392
|
-
const orphanedDeclarations = this.#matchURLPathAgainstOrphanedDeclarations(urlPath)
|
|
393
469
|
|
|
394
|
-
return { functionNames, invocationMetadata
|
|
470
|
+
return { functionNames, invocationMetadata }
|
|
395
471
|
}
|
|
396
472
|
|
|
397
473
|
/**
|
|
474
|
+
* Takes the module graph returned from the server and tracks dependencies of
|
|
475
|
+
* each function.
|
|
398
476
|
*
|
|
399
|
-
* @param {
|
|
400
|
-
* @returns {string[]}
|
|
477
|
+
* @param {ModuleGraph} graph
|
|
401
478
|
*/
|
|
402
|
-
#matchURLPathAgainstOrphanedDeclarations(urlPath) {
|
|
403
|
-
// `generateManifest` will only include functions for which there is both a
|
|
404
|
-
// function file and a config declaration, but we want to catch cases where
|
|
405
|
-
// a config declaration exists without a matching function file. To do that
|
|
406
|
-
// we compute a list of functions from the declarations (the `path` doesn't
|
|
407
|
-
// really matter).
|
|
408
|
-
const functions = this.#declarationsFromTOML.map((declaration) => ({ name: declaration.function, path: '' }))
|
|
409
|
-
const manifest = this.#bundler.generateManifest({
|
|
410
|
-
declarations: this.#declarationsFromTOML,
|
|
411
|
-
userFunctionConfig: this.#userFunctionConfigs,
|
|
412
|
-
internalFunctionConfig: this.#internalFunctionConfigs,
|
|
413
|
-
functions,
|
|
414
|
-
featureFlags,
|
|
415
|
-
})
|
|
416
|
-
|
|
417
|
-
const routes = [...manifest.routes, ...manifest.post_cache_routes].map((route) => ({
|
|
418
|
-
...route,
|
|
419
|
-
pattern: new RegExp(route.pattern),
|
|
420
|
-
}))
|
|
421
|
-
|
|
422
|
-
const functionNames = routes
|
|
423
|
-
.filter((route) => {
|
|
424
|
-
const hasFunctionFile = this.#functions.some((func) => func.name === route.function)
|
|
425
|
-
|
|
426
|
-
if (hasFunctionFile) {
|
|
427
|
-
return false
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
return route.pattern.test(urlPath)
|
|
431
|
-
})
|
|
432
|
-
.map((route) => route.function)
|
|
433
|
-
|
|
434
|
-
return functionNames
|
|
435
|
-
}
|
|
436
|
-
|
|
437
479
|
#processGraph(graph) {
|
|
438
480
|
if (!graph) {
|
|
439
481
|
warn('Could not process edge functions dependency graph. Live reload will not be available.')
|
|
@@ -485,6 +527,33 @@ export class EdgeFunctionsRegistry {
|
|
|
485
527
|
this.#functionPaths = functionPaths
|
|
486
528
|
}
|
|
487
529
|
|
|
530
|
+
/**
|
|
531
|
+
* Thin wrapper for `#runIsolate` that skips running a build and returns an
|
|
532
|
+
* empty response if there are no functions in the registry.
|
|
533
|
+
*/
|
|
534
|
+
async #runBuild() {
|
|
535
|
+
if (this.#functions.length === 0) {
|
|
536
|
+
return {
|
|
537
|
+
functionsConfig: [],
|
|
538
|
+
graph: {
|
|
539
|
+
modules: [],
|
|
540
|
+
},
|
|
541
|
+
npmSpecifiersWithExtraneousFiles: [],
|
|
542
|
+
success: true,
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const { functionsConfig, graph, npmSpecifiersWithExtraneousFiles, success } = await this.#runIsolate(
|
|
547
|
+
this.#functions,
|
|
548
|
+
this.#env,
|
|
549
|
+
{
|
|
550
|
+
getFunctionsConfig: true,
|
|
551
|
+
},
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
return { functionsConfig, graph, npmSpecifiersWithExtraneousFiles, success }
|
|
555
|
+
}
|
|
556
|
+
|
|
488
557
|
/**
|
|
489
558
|
* @returns {Promise<{all: EdgeFunction[], new: EdgeFunction[], deleted: EdgeFunction[]}>}
|
|
490
559
|
*/
|