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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "netlify-cli",
3
3
  "description": "Netlify command line tool",
4
- "version": "15.7.0",
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.12.8",
48
- "@netlify/build-info": "7.0.8",
49
- "@netlify/config": "20.5.1",
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.10.0",
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.0",
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.2",
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.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.11",
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
- this.analytics = { ...this.analytics, payload }
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
- const project = new Project(fs, buildDir)
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
+ }
@@ -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 {*} config.settings
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 {Partial<import('../../utils/types').ServerSettings>} */
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
- log(NETLIFYDEVERR, error_.message)
140
- exit(1)
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(`${key}${value ? '=' : ''}${value}`)}${withScope} in the ${chalk.magenta(
62
- context || 'all',
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
- const scopes = scope || AVAILABLE_SCOPES
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
- const body = { key, scopes, values }
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.48",
30
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.48.tgz",
31
- "integrity": "sha512-iL0PIMwejpmuVHgfibHpfDwOdsbmB50wr21X71VnF5d7SsBF7WK+ZvP/SCcFm7Iwb9iiYSap9rlrdhToNAWdxg=="
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.48",
62
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.48.tgz",
63
- "integrity": "sha512-iL0PIMwejpmuVHgfibHpfDwOdsbmB50wr21X71VnF5d7SsBF7WK+ZvP/SCcFm7Iwb9iiYSap9rlrdhToNAWdxg=="
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://64523ab4e7865600087fc3df--edge.netlify.com/bootstrap/index-combined.ts'
3
+ const latestBootstrapURL = 'https://6494585a67d46e0008867e60--edge.netlify.com/bootstrap/index-combined.ts'
4
4
 
5
5
  export const getBootstrapURL = () => env.NETLIFY_EDGE_BOOTSTRAP || latestBootstrapURL
@@ -11,6 +11,7 @@ export const headers = {
11
11
  IP: 'x-nf-client-connection-ip',
12
12
  Site: 'X-NF-Site-Info',
13
13
  DebugLogging: 'x-nf-debug-logging',
14
+ Account: 'x-nf-account-info',
14
15
  }
15
16
 
16
17
  /**
@@ -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
- // Creating a watcher for the config file. When it changes, we update the
471
- // declarations and see if we need to register or unregister any functions.
472
- this.#configWatcher = await watchDebounced(this.#configPath, {
473
- onChange: async () => {
474
- const newConfig = await this.#getUpdatedConfig()
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
- this.#declarationsFromTOML = EdgeFunctionsRegistry.#getDeclarationsFromTOML(newConfig)
477
+ this.#declarationsFromTOML = EdgeFunctionsRegistry.#getDeclarationsFromTOML(newConfig)
477
478
 
478
- await this.#checkForAddedOrDeletedFunctions()
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
- // For TypeScript we use NFT, otherwise we leave the file untouched with the `none` bundler
139
- const isTypescript = ['.ts', '.mts', '.cts'].includes(path.extname(func.mainFile))
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
- response.end(await processRenderedResponse(err, request))
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) => {
@@ -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 {object} [config.error]
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 {object} - default or custom chalk instance
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 {string|Error} message
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 = message instanceof Error ? message : new Error(message)
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)