netlify-cli 15.7.0 → 15.8.1-rc.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/npm-shrinkwrap.json +5726 -4405
- package/package.json +9 -10
- package/src/commands/base-command.mjs +33 -5
- package/src/commands/dev/dev.mjs +15 -14
- package/src/commands/env/env-set.mjs +50 -10
- package/src/commands/functions/functions-serve.mjs +2 -1
- package/src/commands/serve/serve.mjs +4 -2
- package/src/functions-templates/typescript/hello-world/package-lock.json +6 -6
- package/src/functions-templates/typescript/transform-response/{{name}}.ts +0 -1
- package/src/lib/edge-functions/bootstrap.mjs +1 -1
- package/src/lib/edge-functions/headers.mjs +1 -0
- package/src/lib/edge-functions/proxy.mjs +9 -0
- package/src/lib/edge-functions/registry.mjs +11 -9
- package/src/lib/functions/runtimes/js/builders/zisi.mjs +2 -12
- package/src/lib/functions/server.mjs +1 -0
- package/src/lib/functions/synchronous.mjs +10 -10
- package/src/lib/spinner.mjs +1 -1
- package/src/utils/command-helpers.mjs +10 -3
- package/src/utils/detect-server-settings.mjs +124 -132
- package/src/utils/dev.mjs +1 -0
- package/src/utils/framework-server.mjs +2 -2
- package/src/utils/init/config-github.mjs +2 -2
- package/src/utils/init/config-manual.mjs +2 -2
- package/src/utils/init/frameworks.mjs +10 -7
- package/src/utils/init/utils.mjs +6 -5
- package/src/utils/proxy-server.mjs +2 -0
- package/src/utils/proxy.mjs +29 -2
- package/src/utils/run-build.mjs +44 -4
- package/src/utils/shell.mjs +13 -2
- package/src/utils/static-server.mjs +4 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.8.1-rc.0",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
@@ -44,14 +44,13 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@bugsnag/js": "7.20.2",
|
|
46
46
|
"@fastify/static": "6.10.2",
|
|
47
|
-
"@netlify/build": "29.
|
|
48
|
-
"@netlify/build-info": "7.
|
|
49
|
-
"@netlify/config": "20.5.
|
|
47
|
+
"@netlify/build": "29.15.4",
|
|
48
|
+
"@netlify/build-info": "7.4.1",
|
|
49
|
+
"@netlify/config": "20.5.2",
|
|
50
50
|
"@netlify/edge-bundler": "8.16.2",
|
|
51
|
-
"@netlify/framework-info": "9.8.10",
|
|
52
51
|
"@netlify/local-functions-proxy": "1.1.1",
|
|
53
52
|
"@netlify/serverless-functions-api": "1.5.1",
|
|
54
|
-
"@netlify/zip-it-and-ship-it": "9.
|
|
53
|
+
"@netlify/zip-it-and-ship-it": "9.12.2",
|
|
55
54
|
"@octokit/rest": "19.0.13",
|
|
56
55
|
"@skn0tt/lambda-local": "2.0.3",
|
|
57
56
|
"ansi-escapes": "6.2.0",
|
|
@@ -60,7 +59,7 @@
|
|
|
60
59
|
"ascii-table": "0.0.9",
|
|
61
60
|
"backoff": "2.5.0",
|
|
62
61
|
"better-opn": "3.0.2",
|
|
63
|
-
"boxen": "7.1.
|
|
62
|
+
"boxen": "7.1.1",
|
|
64
63
|
"chalk": "5.2.0",
|
|
65
64
|
"chokidar": "3.5.3",
|
|
66
65
|
"ci-info": "3.8.0",
|
|
@@ -92,7 +91,7 @@
|
|
|
92
91
|
"from2-array": "0.0.4",
|
|
93
92
|
"fuzzy": "0.1.3",
|
|
94
93
|
"get-port": "5.1.1",
|
|
95
|
-
"gh-release-fetch": "4.0.
|
|
94
|
+
"gh-release-fetch": "4.0.3",
|
|
96
95
|
"git-repo-info": "2.1.1",
|
|
97
96
|
"gitconfiglocal": "2.1.0",
|
|
98
97
|
"hasbin": "1.2.3",
|
|
@@ -106,7 +105,7 @@
|
|
|
106
105
|
"is-stream": "3.0.0",
|
|
107
106
|
"is-wsl": "2.2.0",
|
|
108
107
|
"isexe": "2.0.0",
|
|
109
|
-
"jsonwebtoken": "9.0.
|
|
108
|
+
"jsonwebtoken": "9.0.1",
|
|
110
109
|
"jwt-decode": "3.1.2",
|
|
111
110
|
"listr": "0.14.3",
|
|
112
111
|
"locate-path": "7.2.0",
|
|
@@ -119,7 +118,7 @@
|
|
|
119
118
|
"netlify-headers-parser": "7.1.2",
|
|
120
119
|
"netlify-redirect-parser": "14.1.3",
|
|
121
120
|
"netlify-redirector": "0.4.0",
|
|
122
|
-
"node-fetch": "2.6.
|
|
121
|
+
"node-fetch": "2.6.12",
|
|
123
122
|
"node-version-alias": "3.4.1",
|
|
124
123
|
"ora": "6.3.1",
|
|
125
124
|
"p-filter": "3.0.0",
|
|
@@ -8,6 +8,7 @@ import { NodeFS } from '@netlify/build-info/node'
|
|
|
8
8
|
import { resolveConfig } from '@netlify/config'
|
|
9
9
|
import { Command, Option } from 'commander'
|
|
10
10
|
import debug from 'debug'
|
|
11
|
+
import execa from 'execa'
|
|
11
12
|
import merge from 'lodash/merge.js'
|
|
12
13
|
import { NetlifyAPI } from 'netlify'
|
|
13
14
|
|
|
@@ -88,6 +89,9 @@ export default class BaseCommand extends Command {
|
|
|
88
89
|
/** @type {{ startTime: bigint, payload?: any}} */
|
|
89
90
|
analytics = { startTime: process.hrtime.bigint() }
|
|
90
91
|
|
|
92
|
+
/** @type {Project} */
|
|
93
|
+
project
|
|
94
|
+
|
|
91
95
|
/**
|
|
92
96
|
* IMPORTANT this function will be called for each command!
|
|
93
97
|
* Don't do anything expensive in there.
|
|
@@ -337,6 +341,11 @@ export default class BaseCommand extends Command {
|
|
|
337
341
|
}
|
|
338
342
|
}
|
|
339
343
|
|
|
344
|
+
/**
|
|
345
|
+
*
|
|
346
|
+
* @param {string|undefined} tokenFromFlag
|
|
347
|
+
* @returns
|
|
348
|
+
*/
|
|
340
349
|
async authenticate(tokenFromFlag) {
|
|
341
350
|
const [token] = await getToken(tokenFromFlag)
|
|
342
351
|
if (token) {
|
|
@@ -407,7 +416,8 @@ export default class BaseCommand extends Command {
|
|
|
407
416
|
}
|
|
408
417
|
|
|
409
418
|
setAnalyticsPayload(payload) {
|
|
410
|
-
|
|
419
|
+
const newPayload = { ...this.analytics.payload, ...payload }
|
|
420
|
+
this.analytics = { ...this.analytics, payload: newPayload }
|
|
411
421
|
}
|
|
412
422
|
|
|
413
423
|
/**
|
|
@@ -459,10 +469,12 @@ export default class BaseCommand extends Command {
|
|
|
459
469
|
|
|
460
470
|
const globalConfig = await getGlobalConfig()
|
|
461
471
|
|
|
472
|
+
// retrieve the repository root
|
|
473
|
+
const rootDir = await getRepositoryRoot()
|
|
462
474
|
// Get framework, add to analytics payload for every command, if a framework is set
|
|
463
475
|
const fs = new NodeFS()
|
|
464
|
-
|
|
465
|
-
const frameworks = await project.detectFrameworks()
|
|
476
|
+
this.project = new Project(fs, buildDir, rootDir).setEnvironment(process.env).setNodeVersion(process.version)
|
|
477
|
+
const frameworks = await this.project.detectFrameworks()
|
|
466
478
|
|
|
467
479
|
const frameworkIDs = frameworks?.map((framework) => framework.id)
|
|
468
480
|
|
|
@@ -471,10 +483,12 @@ export default class BaseCommand extends Command {
|
|
|
471
483
|
}
|
|
472
484
|
|
|
473
485
|
this.setAnalyticsPayload({
|
|
474
|
-
packageManager: project.packageManager?.name,
|
|
475
|
-
buildSystem: project.buildSystems.map(({ id }) => id),
|
|
486
|
+
packageManager: this.project.packageManager?.name,
|
|
487
|
+
buildSystem: this.project.buildSystems.map(({ id }) => id),
|
|
476
488
|
})
|
|
477
489
|
|
|
490
|
+
actionCommand.project = this.project
|
|
491
|
+
|
|
478
492
|
actionCommand.netlify = {
|
|
479
493
|
// api methods
|
|
480
494
|
api,
|
|
@@ -567,3 +581,17 @@ export default class BaseCommand extends Command {
|
|
|
567
581
|
return 'dev'
|
|
568
582
|
}
|
|
569
583
|
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Retrieves the repository root through a git command.
|
|
587
|
+
* Returns undefined if not a git project.
|
|
588
|
+
* @returns {Promise<string|undefined>}
|
|
589
|
+
*/
|
|
590
|
+
async function getRepositoryRoot() {
|
|
591
|
+
try {
|
|
592
|
+
const res = await execa('git', ['rev-parse', '--show-toplevel'], { preferLocal: true })
|
|
593
|
+
return res.stdout
|
|
594
|
+
} catch {
|
|
595
|
+
// noop
|
|
596
|
+
}
|
|
597
|
+
}
|
package/src/commands/dev/dev.mjs
CHANGED
|
@@ -9,7 +9,6 @@ import { printBanner } from '../../utils/banner.mjs'
|
|
|
9
9
|
import {
|
|
10
10
|
BANG,
|
|
11
11
|
chalk,
|
|
12
|
-
exit,
|
|
13
12
|
log,
|
|
14
13
|
NETLIFYDEV,
|
|
15
14
|
NETLIFYDEVERR,
|
|
@@ -35,7 +34,7 @@ import { createDevExecCommand } from './dev-exec.mjs'
|
|
|
35
34
|
* @param {object} config
|
|
36
35
|
* @param {*} config.api
|
|
37
36
|
* @param {import('commander').OptionValues} config.options
|
|
38
|
-
* @param {
|
|
37
|
+
* @param {import('../../utils/types.js').ServerSettings} config.settings
|
|
39
38
|
* @param {*} config.site
|
|
40
39
|
* @param {*} config.state
|
|
41
40
|
* @returns
|
|
@@ -68,6 +67,9 @@ const handleLiveTunnel = async ({ api, options, settings, site, state }) => {
|
|
|
68
67
|
}
|
|
69
68
|
}
|
|
70
69
|
|
|
70
|
+
/**
|
|
71
|
+
* @param {string} args
|
|
72
|
+
*/
|
|
71
73
|
const validateShortFlagArgs = (args) => {
|
|
72
74
|
if (args.startsWith('=')) {
|
|
73
75
|
throw new Error(
|
|
@@ -94,7 +96,7 @@ const dev = async (options, command) => {
|
|
|
94
96
|
const { api, cachedConfig, config, repositoryRoot, site, siteInfo, state } = command.netlify
|
|
95
97
|
config.dev = { ...config.dev }
|
|
96
98
|
config.build = { ...config.build }
|
|
97
|
-
/** @type {import('./types').DevConfig} */
|
|
99
|
+
/** @type {import('./types.js').DevConfig} */
|
|
98
100
|
const devConfig = {
|
|
99
101
|
framework: '#auto',
|
|
100
102
|
...(config.functionsDirectory && { functions: config.functionsDirectory }),
|
|
@@ -116,7 +118,7 @@ const dev = async (options, command) => {
|
|
|
116
118
|
injectEnvVariables(env)
|
|
117
119
|
await promptEditorHelper({ chalk, config, log, NETLIFYDEVLOG, repositoryRoot, state })
|
|
118
120
|
|
|
119
|
-
const { addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
121
|
+
const { accountId, addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
120
122
|
// inherited from base command --offline
|
|
121
123
|
offline: options.offline,
|
|
122
124
|
api,
|
|
@@ -124,20 +126,17 @@ const dev = async (options, command) => {
|
|
|
124
126
|
siteInfo,
|
|
125
127
|
})
|
|
126
128
|
|
|
127
|
-
/** @type {
|
|
128
|
-
let settings
|
|
129
|
+
/** @type {import('../../utils/types.js').ServerSettings} */
|
|
130
|
+
let settings
|
|
129
131
|
try {
|
|
130
|
-
settings = await detectServerSettings(devConfig, options, site.root
|
|
131
|
-
site: {
|
|
132
|
-
id: site.id,
|
|
133
|
-
url: siteUrl,
|
|
134
|
-
},
|
|
135
|
-
})
|
|
132
|
+
settings = await detectServerSettings(devConfig, options, command.project, site.root)
|
|
136
133
|
|
|
137
134
|
cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings)
|
|
138
135
|
} catch (error_) {
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
if (error_ && typeof error_ === 'object' && 'message' in error_) {
|
|
137
|
+
log(NETLIFYDEVERR, error_.message)
|
|
138
|
+
}
|
|
139
|
+
process.exit(1)
|
|
141
140
|
}
|
|
142
141
|
|
|
143
142
|
command.setAnalyticsPayload({ live: options.live })
|
|
@@ -176,6 +175,7 @@ const dev = async (options, command) => {
|
|
|
176
175
|
geoCountry: options.country,
|
|
177
176
|
offline: options.offline,
|
|
178
177
|
state,
|
|
178
|
+
accountId,
|
|
179
179
|
})
|
|
180
180
|
|
|
181
181
|
// Try to add `.netlify` to `.gitignore`.
|
|
@@ -211,6 +211,7 @@ const dev = async (options, command) => {
|
|
|
211
211
|
state,
|
|
212
212
|
geolocationMode: options.geo,
|
|
213
213
|
geoCountry: options.country,
|
|
214
|
+
accountId,
|
|
214
215
|
})
|
|
215
216
|
|
|
216
217
|
if (devConfig.autoLaunch !== false) {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
* @returns {Promise<boolean>}
|
|
19
19
|
*/
|
|
20
20
|
const envSet = async (key, value, options, command) => {
|
|
21
|
-
const { context, scope } = options
|
|
21
|
+
const { context, scope, secret } = options
|
|
22
22
|
|
|
23
23
|
const { api, cachedConfig, site } = command.netlify
|
|
24
24
|
const siteId = site.id
|
|
@@ -33,7 +33,7 @@ const envSet = async (key, value, options, command) => {
|
|
|
33
33
|
|
|
34
34
|
// Get current environment variables set in the UI
|
|
35
35
|
if (siteInfo.use_envelope) {
|
|
36
|
-
finalEnv = await setInEnvelope({ api, siteInfo, key, value, context, scope })
|
|
36
|
+
finalEnv = await setInEnvelope({ api, siteInfo, key, value, context, scope, secret })
|
|
37
37
|
} else if (context || scope) {
|
|
38
38
|
error(
|
|
39
39
|
`To specify a context or scope, please run ${chalk.yellow(
|
|
@@ -56,11 +56,12 @@ const envSet = async (key, value, options, command) => {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const withScope = scope ? ` scoped to ${chalk.white(scope)}` : ''
|
|
59
|
+
const withSecret = secret ? ` as a ${chalk.blue('secret')}` : ''
|
|
59
60
|
const contextType = AVAILABLE_CONTEXTS.includes(context || 'all') ? 'context' : 'branch'
|
|
60
61
|
log(
|
|
61
|
-
`Set environment variable ${chalk.yellow(
|
|
62
|
-
|
|
63
|
-
)} ${contextType}`,
|
|
62
|
+
`Set environment variable ${chalk.yellow(
|
|
63
|
+
`${key}${value && !secret ? `=${value}` : ''}`,
|
|
64
|
+
)}${withScope}${withSecret} in the ${chalk.magenta(context || 'all')} ${contextType}`,
|
|
64
65
|
)
|
|
65
66
|
}
|
|
66
67
|
|
|
@@ -88,15 +89,35 @@ const setInMongo = async ({ api, key, siteInfo, value }) => {
|
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
91
|
* Updates the env for a site configured with Envelope with a new key/value pair
|
|
91
|
-
* @returns {Promise<object>}
|
|
92
|
+
* @returns {Promise<object | boolean>}
|
|
92
93
|
*/
|
|
93
|
-
const setInEnvelope = async ({ api, context, key, scope, siteInfo, value }) => {
|
|
94
|
+
const setInEnvelope = async ({ api, context, key, scope, secret, siteInfo, value }) => {
|
|
94
95
|
const accountId = siteInfo.account_slug
|
|
95
96
|
const siteId = siteInfo.id
|
|
97
|
+
|
|
98
|
+
// secret values may not be used in the post-processing scope
|
|
99
|
+
if (secret && scope && scope.some((sco) => /post[-_]processing/.test(sco))) {
|
|
100
|
+
error(`Secret values cannot be used within the post-processing scope.`)
|
|
101
|
+
return false
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// secret values must specify deploy contexts. `all` or `dev` are not allowed
|
|
105
|
+
if (secret && value && (!context || context.includes('dev'))) {
|
|
106
|
+
error(
|
|
107
|
+
`To set a secret environment variable value, please specify a non-development context with the \`--context\` flag.`,
|
|
108
|
+
)
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
|
|
96
112
|
// fetch envelope env vars
|
|
97
113
|
const envelopeVariables = await api.getEnvVars({ accountId, siteId })
|
|
98
114
|
const contexts = context || ['all']
|
|
99
|
-
|
|
115
|
+
let scopes = scope || AVAILABLE_SCOPES
|
|
116
|
+
|
|
117
|
+
if (secret) {
|
|
118
|
+
// post_processing (aka post-processing) scope is not allowed with secrets
|
|
119
|
+
scopes = scopes.filter((sco) => !/post[-_]processing/.test(sco))
|
|
120
|
+
}
|
|
100
121
|
|
|
101
122
|
// if the passed context is unknown, it is actually a branch name
|
|
102
123
|
let values = contexts.map((ctx) =>
|
|
@@ -111,6 +132,10 @@ const setInEnvelope = async ({ api, context, key, scope, siteInfo, value }) => {
|
|
|
111
132
|
if (!value) {
|
|
112
133
|
// eslint-disable-next-line prefer-destructuring
|
|
113
134
|
values = existing.values
|
|
135
|
+
if (!scope) {
|
|
136
|
+
// eslint-disable-next-line prefer-destructuring
|
|
137
|
+
scopes = existing.scopes
|
|
138
|
+
}
|
|
114
139
|
}
|
|
115
140
|
if (context && scope) {
|
|
116
141
|
error(
|
|
@@ -123,12 +148,24 @@ const setInEnvelope = async ({ api, context, key, scope, siteInfo, value }) => {
|
|
|
123
148
|
await Promise.all(values.map((val) => api.setEnvVarValue({ ...params, body: val })))
|
|
124
149
|
} else {
|
|
125
150
|
// otherwise update whole env var
|
|
126
|
-
|
|
151
|
+
if (secret) {
|
|
152
|
+
scopes = scopes.filter((sco) => !/post[-_]processing/.test(sco))
|
|
153
|
+
if (values.some((val) => val.context === 'all')) {
|
|
154
|
+
log(`This secret's value will be empty in the dev context.`)
|
|
155
|
+
log(`Run \`netlify env:set ${key} <value> --context dev\` to set a new value for the dev context.`)
|
|
156
|
+
values = AVAILABLE_CONTEXTS.filter((ctx) => ctx !== 'all').map((ctx) => ({
|
|
157
|
+
context: ctx,
|
|
158
|
+
// empty out dev value so that secret is indeed secret
|
|
159
|
+
value: ctx === 'dev' ? '' : values.find((val) => val.context === 'all').value,
|
|
160
|
+
}))
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const body = { key, is_secret: secret, scopes, values }
|
|
127
164
|
await api.updateEnvVar({ ...params, body })
|
|
128
165
|
}
|
|
129
166
|
} else {
|
|
130
167
|
// create whole env var
|
|
131
|
-
const body = [{ key, scopes, values }]
|
|
168
|
+
const body = [{ key, is_secret: secret, scopes, values }]
|
|
132
169
|
await api.createEnvVars({ ...params, body })
|
|
133
170
|
}
|
|
134
171
|
} catch (error_) {
|
|
@@ -166,13 +203,16 @@ export const createEnvSetCommand = (program) =>
|
|
|
166
203
|
'runtime',
|
|
167
204
|
]),
|
|
168
205
|
)
|
|
206
|
+
.option('--secret', 'Indicate whether the environment variable value can be read again.')
|
|
169
207
|
.description('Set value of environment variable')
|
|
170
208
|
.addExamples([
|
|
171
209
|
'netlify env:set VAR_NAME value # set in all contexts and scopes',
|
|
172
210
|
'netlify env:set VAR_NAME value --context production',
|
|
173
211
|
'netlify env:set VAR_NAME value --context production deploy-preview',
|
|
212
|
+
'netlify env:set VAR_NAME value --context production --secret',
|
|
174
213
|
'netlify env:set VAR_NAME value --scope builds',
|
|
175
214
|
'netlify env:set VAR_NAME value --scope builds functions',
|
|
215
|
+
'netlify env:set VAR_NAME --secret # convert existing variable to secret',
|
|
176
216
|
])
|
|
177
217
|
.action(async (key, value, options, command) => {
|
|
178
218
|
await envSet(key, value, options, command)
|
|
@@ -23,7 +23,7 @@ const functionsServe = async (options, command) => {
|
|
|
23
23
|
env = await getDotEnvVariables({ devConfig: { ...config.dev }, env, site })
|
|
24
24
|
injectEnvVariables(env)
|
|
25
25
|
|
|
26
|
-
const { capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
26
|
+
const { accountId, capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
27
27
|
offline: options.offline,
|
|
28
28
|
api,
|
|
29
29
|
site,
|
|
@@ -52,6 +52,7 @@ const functionsServe = async (options, command) => {
|
|
|
52
52
|
geoCountry: options.country,
|
|
53
53
|
offline: options.offline,
|
|
54
54
|
state,
|
|
55
|
+
accountId,
|
|
55
56
|
})
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -56,7 +56,7 @@ const serve = async (options, command) => {
|
|
|
56
56
|
injectEnvVariables(env)
|
|
57
57
|
await promptEditorHelper({ chalk, config, log, NETLIFYDEVLOG, repositoryRoot, state })
|
|
58
58
|
|
|
59
|
-
const { addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
59
|
+
const { accountId, addonsUrls, capabilities, siteUrl, timeouts } = await getSiteInformation({
|
|
60
60
|
// inherited from base command --offline
|
|
61
61
|
offline: options.offline,
|
|
62
62
|
api,
|
|
@@ -72,7 +72,7 @@ const serve = async (options, command) => {
|
|
|
72
72
|
/** @type {Partial<import('../../utils/types').ServerSettings>} */
|
|
73
73
|
let settings = {}
|
|
74
74
|
try {
|
|
75
|
-
settings = await detectServerSettings(devConfig, options, site.root)
|
|
75
|
+
settings = await detectServerSettings(devConfig, options, command.project, site.root)
|
|
76
76
|
|
|
77
77
|
cachedConfig.config = getConfigWithPlugins(cachedConfig.config, settings)
|
|
78
78
|
} catch (error_) {
|
|
@@ -105,6 +105,7 @@ const serve = async (options, command) => {
|
|
|
105
105
|
geoCountry: options.country,
|
|
106
106
|
offline: options.offline,
|
|
107
107
|
state,
|
|
108
|
+
accountId,
|
|
108
109
|
})
|
|
109
110
|
|
|
110
111
|
// Try to add `.netlify` to `.gitignore`.
|
|
@@ -138,6 +139,7 @@ const serve = async (options, command) => {
|
|
|
138
139
|
site,
|
|
139
140
|
siteInfo,
|
|
140
141
|
state,
|
|
142
|
+
accountId,
|
|
141
143
|
})
|
|
142
144
|
|
|
143
145
|
if (devConfig.autoLaunch !== false) {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
"node_modules/@types/node": {
|
|
29
|
-
"version": "14.18.
|
|
30
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.
|
|
31
|
-
"integrity": "sha512-
|
|
29
|
+
"version": "14.18.53",
|
|
30
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.53.tgz",
|
|
31
|
+
"integrity": "sha512-soGmOpVBUq+gaBMwom1M+krC/NNbWlosh4AtGA03SyWNDiqSKtwp7OulO1M6+mg8YkHMvJ/y0AkCeO8d1hNb7A=="
|
|
32
32
|
},
|
|
33
33
|
"node_modules/is-promise": {
|
|
34
34
|
"version": "4.0.0",
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"@types/node": {
|
|
61
|
-
"version": "14.18.
|
|
62
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.
|
|
63
|
-
"integrity": "sha512-
|
|
61
|
+
"version": "14.18.53",
|
|
62
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.53.tgz",
|
|
63
|
+
"integrity": "sha512-soGmOpVBUq+gaBMwom1M+krC/NNbWlosh4AtGA03SyWNDiqSKtwp7OulO1M6+mg8YkHMvJ/y0AkCeO8d1hNb7A=="
|
|
64
64
|
},
|
|
65
65
|
"is-promise": {
|
|
66
66
|
"version": "4.0.0",
|
|
@@ -7,7 +7,6 @@ export default async (request: Request, context: Context) => {
|
|
|
7
7
|
if (url.searchParams.get("method") !== "transform") {
|
|
8
8
|
return;
|
|
9
9
|
}
|
|
10
|
-
|
|
11
10
|
const response = await context.next();
|
|
12
11
|
const text = await response.text();
|
|
13
12
|
return new Response(text.toUpperCase(), response);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { env } from 'process'
|
|
2
2
|
|
|
3
|
-
const latestBootstrapURL = 'https://
|
|
3
|
+
const latestBootstrapURL = 'https://6494585a67d46e0008867e60--edge.netlify.com/bootstrap/index-combined.ts'
|
|
4
4
|
|
|
5
5
|
export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL
|
|
@@ -53,7 +53,15 @@ export const createSiteInfoHeader = (siteInfo = {}) => {
|
|
|
53
53
|
return Buffer.from(siteString).toString('base64')
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
export const createAccountInfoHeader = (accountInfo = {}) => {
|
|
57
|
+
const { id } = accountInfo
|
|
58
|
+
const account = { id }
|
|
59
|
+
const accountString = JSON.stringify(account)
|
|
60
|
+
return Buffer.from(accountString).toString('base64')
|
|
61
|
+
}
|
|
62
|
+
|
|
56
63
|
export const initializeProxy = async ({
|
|
64
|
+
accountId,
|
|
57
65
|
config,
|
|
58
66
|
configPath,
|
|
59
67
|
debug,
|
|
@@ -106,6 +114,7 @@ export const initializeProxy = async ({
|
|
|
106
114
|
// Setting header with geolocation and site info.
|
|
107
115
|
req.headers[headers.Geo] = JSON.stringify(geoLocation)
|
|
108
116
|
req.headers[headers.Site] = createSiteInfoHeader(siteInfo)
|
|
117
|
+
req.headers[headers.Account] = createAccountInfoHeader({ id: accountId })
|
|
109
118
|
|
|
110
119
|
await registry.initialize()
|
|
111
120
|
|
|
@@ -467,17 +467,19 @@ export class EdgeFunctionsRegistry {
|
|
|
467
467
|
* @param {string} projectDir
|
|
468
468
|
*/
|
|
469
469
|
async #setupWatchers(projectDir) {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
470
|
+
if (this.#configPath) {
|
|
471
|
+
// Creating a watcher for the config file. When it changes, we update the
|
|
472
|
+
// declarations and see if we need to register or unregister any functions.
|
|
473
|
+
this.#configWatcher = await watchDebounced(this.#configPath, {
|
|
474
|
+
onChange: async () => {
|
|
475
|
+
const newConfig = await this.#getUpdatedConfig()
|
|
475
476
|
|
|
476
|
-
|
|
477
|
+
this.#declarationsFromTOML = EdgeFunctionsRegistry.#getDeclarationsFromTOML(newConfig)
|
|
477
478
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
479
|
+
await this.#checkForAddedOrDeletedFunctions()
|
|
480
|
+
},
|
|
481
|
+
})
|
|
482
|
+
}
|
|
481
483
|
|
|
482
484
|
// While functions are guaranteed to be inside one of the configured
|
|
483
485
|
// directories, they might be importing files that are located in
|
|
@@ -135,18 +135,8 @@ export default async function handler({ config, directory, errorExit, func, meta
|
|
|
135
135
|
const featureFlags = {}
|
|
136
136
|
|
|
137
137
|
if (metadata.runtimeAPIVersion === 2) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (isTypescript) {
|
|
142
|
-
functionsConfig['*'].nodeBundler = 'nft'
|
|
143
|
-
} else {
|
|
144
|
-
// using esbuild is less performant than `none`, but it emits sourcemaps and thus
|
|
145
|
-
// enables debugging functions
|
|
146
|
-
functionsConfig['*'].nodeBundler = 'esbuild'
|
|
147
|
-
featureFlags.zisi_pure_esm = true
|
|
148
|
-
featureFlags.zisi_pure_esm_mjs = true
|
|
149
|
-
}
|
|
138
|
+
featureFlags.zisi_pure_esm = true
|
|
139
|
+
featureFlags.zisi_pure_esm_mjs = true
|
|
150
140
|
} else {
|
|
151
141
|
// We must use esbuild for certain file extensions.
|
|
152
142
|
const mustTranspile = ['.mjs', '.ts', '.mts', '.cts'].includes(path.extname(func.mainFile))
|
|
@@ -111,6 +111,7 @@ export const createHandler = function (options) {
|
|
|
111
111
|
...request.headers,
|
|
112
112
|
'client-ip': [remoteAddress],
|
|
113
113
|
'x-nf-client-connection-ip': [remoteAddress],
|
|
114
|
+
'x-nf-account-id': [options.accountId],
|
|
114
115
|
[efHeaders.Geo]: JSON.stringify(geoLocation),
|
|
115
116
|
}).reduce((prev, [key, value]) => ({ ...prev, [key]: Array.isArray(value) ? value : [value] }), {})
|
|
116
117
|
const rawQuery = new URLSearchParams(requestQuery).toString()
|
|
@@ -75,20 +75,20 @@ const formatLambdaLocalError = (err, acceptsHtml) =>
|
|
|
75
75
|
})
|
|
76
76
|
: `${err.errorType}: ${err.errorMessage}\n ${err.stackTrace?.join('\n ')}`
|
|
77
77
|
|
|
78
|
-
const processRenderedResponse = async (err, request) => {
|
|
79
|
-
const acceptsHtml = request.headers && request.headers.accept && request.headers.accept.includes('text/html')
|
|
80
|
-
const errorString = typeof err === 'string' ? err : formatLambdaLocalError(err, acceptsHtml)
|
|
81
|
-
|
|
82
|
-
return acceptsHtml
|
|
83
|
-
? await renderErrorTemplate(errorString, './templates/function-error.html', 'function')
|
|
84
|
-
: errorString
|
|
85
|
-
}
|
|
86
|
-
|
|
87
78
|
const handleErr = async (err, request, response) => {
|
|
88
79
|
detectAwsSdkError({ err })
|
|
89
80
|
|
|
81
|
+
const acceptsHtml = request.headers && request.headers.accept && request.headers.accept.includes('text/html')
|
|
82
|
+
const errorString = typeof err === 'string' ? err : formatLambdaLocalError(err, acceptsHtml)
|
|
83
|
+
|
|
90
84
|
response.statusCode = 500
|
|
91
|
-
|
|
85
|
+
|
|
86
|
+
if (acceptsHtml) {
|
|
87
|
+
response.setHeader('Content-Type', 'text/html')
|
|
88
|
+
response.end(await renderErrorTemplate(errorString, './templates/function-error.html', 'function'))
|
|
89
|
+
} else {
|
|
90
|
+
response.end(errorString)
|
|
91
|
+
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
const validateLambdaResponse = (lambdaResponse) => {
|
package/src/lib/spinner.mjs
CHANGED
|
@@ -17,7 +17,7 @@ export const startSpinner = ({ text }) =>
|
|
|
17
17
|
* Stops the spinner with the following text
|
|
18
18
|
* @param {object} config
|
|
19
19
|
* @param {ora.Ora} config.spinner
|
|
20
|
-
* @param {
|
|
20
|
+
* @param {boolean} [config.error]
|
|
21
21
|
* @param {string} [config.text]
|
|
22
22
|
* @returns {void}
|
|
23
23
|
*/
|
|
@@ -24,7 +24,7 @@ const argv = process.argv.slice(2)
|
|
|
24
24
|
* Chalk instance for CLI that can be initialized with no colors mode
|
|
25
25
|
* needed for json outputs where we don't want to have colors
|
|
26
26
|
* @param {boolean} noColors - disable chalk colors
|
|
27
|
-
* @return {
|
|
27
|
+
* @return {import('chalk').ChalkInstance} - default or custom chalk instance
|
|
28
28
|
*/
|
|
29
29
|
const safeChalk = function (noColors) {
|
|
30
30
|
if (noColors) {
|
|
@@ -174,12 +174,19 @@ export const warn = (message = '') => {
|
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
176
|
* throws an error or log it
|
|
177
|
-
* @param {
|
|
177
|
+
* @param {unknown} message
|
|
178
178
|
* @param {object} [options]
|
|
179
179
|
* @param {boolean} [options.exit]
|
|
180
180
|
*/
|
|
181
181
|
export const error = (message = '', options = {}) => {
|
|
182
|
-
const err =
|
|
182
|
+
const err =
|
|
183
|
+
message instanceof Error
|
|
184
|
+
? message
|
|
185
|
+
: // eslint-disable-next-line unicorn/no-nested-ternary
|
|
186
|
+
typeof message === 'string'
|
|
187
|
+
? new Error(message)
|
|
188
|
+
: // eslint-disable-next-line no-inline-comments
|
|
189
|
+
/** @type {Error} */ ({ message, stack: undefined, name: 'Error' })
|
|
183
190
|
|
|
184
191
|
if (options.exit === false) {
|
|
185
192
|
const bang = chalk.red(BANG)
|