netlify-cli 15.3.2 → 15.4.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.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "netlify-cli",
3
- "version": "15.3.2",
3
+ "version": "15.4.0",
4
4
  "lockfileVersion": 2,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "netlify-cli",
9
- "version": "15.3.2",
9
+ "version": "15.4.0",
10
10
  "hasInstallScript": true,
11
11
  "license": "MIT",
12
12
  "dependencies": {
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.3.2",
4
+ "version": "15.4.0",
5
5
  "author": "Netlify Inc.",
6
6
  "type": "module",
7
7
  "engines": {
@@ -21,7 +21,7 @@ import detectServerSettings, { getConfigWithPlugins } from '../../utils/detect-s
21
21
  import { getDotEnvVariables, getSiteInformation, injectEnvVariables } from '../../utils/dev.mjs'
22
22
  import { getEnvelopeEnv, normalizeContext } from '../../utils/env/index.mjs'
23
23
  import { ensureNetlifyIgnore } from '../../utils/gitignore.mjs'
24
- import { startLiveTunnel } from '../../utils/live-tunnel.mjs'
24
+ import { getLiveTunnelSlug, startLiveTunnel } from '../../utils/live-tunnel.mjs'
25
25
  import openBrowser from '../../utils/open-browser.mjs'
26
26
  import { generateInspectSettings, startProxyServer } from '../../utils/proxy-server.mjs'
27
27
  import { getProxyUrl } from '../../utils/proxy.mjs'
@@ -37,16 +37,33 @@ import { createDevExecCommand } from './dev-exec.mjs'
37
37
  * @param {import('commander').OptionValues} config.options
38
38
  * @param {*} config.settings
39
39
  * @param {*} config.site
40
+ * @param {*} config.state
40
41
  * @returns
41
42
  */
42
- const handleLiveTunnel = async ({ api, options, settings, site }) => {
43
- if (options.live) {
43
+ const handleLiveTunnel = async ({ api, options, settings, site, state }) => {
44
+ const { live } = options
45
+
46
+ if (live) {
47
+ const customSlug = typeof live === 'string' && live.length !== 0 ? live : undefined
48
+ const slug = getLiveTunnelSlug(state, customSlug)
49
+
50
+ let message = `${NETLIFYDEVWARN} Creating live URL with ID ${chalk.yellow(slug)}`
51
+
52
+ if (!customSlug) {
53
+ message += ` (to generate a custom URL, use ${chalk.magenta('--live=<subdomain>')})`
54
+ }
55
+
56
+ log(message)
57
+
44
58
  const sessionUrl = await startLiveTunnel({
45
59
  siteId: site.id,
46
60
  netlifyApiToken: api.accessToken,
47
61
  localPort: settings.port,
62
+ slug,
48
63
  })
64
+
49
65
  process.env.BASE_URL = sessionUrl
66
+
50
67
  return sessionUrl
51
68
  }
52
69
  }
@@ -125,8 +142,9 @@ const dev = async (options, command) => {
125
142
 
126
143
  command.setAnalyticsPayload({ live: options.live })
127
144
 
128
- const liveTunnelUrl = await handleLiveTunnel({ options, site, api, settings })
145
+ const liveTunnelUrl = await handleLiveTunnel({ options, site, api, settings, state })
129
146
  const url = liveTunnelUrl || getProxyUrl(settings)
147
+
130
148
  process.env.URL = url
131
149
  process.env.DEPLOY_URL = url
132
150
 
@@ -229,7 +247,11 @@ export const createDevCommand = (program) => {
229
247
  .option('-d ,--dir <path>', 'dir with static files')
230
248
  .option('-f ,--functions <folder>', 'specify a functions folder to serve')
231
249
  .option('-o ,--offline', 'disables any features that require network access')
232
- .option('-l, --live', 'start a public live session', false)
250
+ .option(
251
+ '-l, --live [subdomain]',
252
+ 'start a public live session; optionally, supply a subdomain to generate a custom URL',
253
+ false,
254
+ )
233
255
  .addOption(
234
256
  new Option('--functionsPort <port>', 'Old, prefer --functions-port. Port of functions server')
235
257
  .argParser((value) => Number.parseInt(value))
@@ -3,6 +3,7 @@ import process from 'process'
3
3
 
4
4
  import fetch from 'node-fetch'
5
5
  import pWaitFor from 'p-wait-for'
6
+ import { v4 as uuidv4 } from 'uuid'
6
7
 
7
8
  import { fetchLatestVersion, shouldFetchLatestVersion } from '../lib/exec-fetcher.mjs'
8
9
  import { getPathInHome } from '../lib/settings.mjs'
@@ -12,13 +13,14 @@ import execa from './execa.mjs'
12
13
 
13
14
  const PACKAGE_NAME = 'live-tunnel-client'
14
15
  const EXEC_NAME = PACKAGE_NAME
16
+ const SLUG_LOCAL_STATE_KEY = 'liveTunnelSlug'
15
17
 
16
18
  // 1 second
17
19
  const TUNNEL_POLL_INTERVAL = 1e3
18
20
  // 5 minutes
19
21
  const TUNNEL_POLL_TIMEOUT = 3e5
20
22
 
21
- const createTunnel = async function ({ netlifyApiToken, siteId }) {
23
+ const createTunnel = async function ({ netlifyApiToken, siteId, slug }) {
22
24
  await installTunnelClient()
23
25
 
24
26
  if (!siteId) {
@@ -29,9 +31,8 @@ const createTunnel = async function ({ netlifyApiToken, siteId }) {
29
31
  )
30
32
  process.exit(1)
31
33
  }
32
- log(`${NETLIFYDEVLOG} Creating Live Tunnel for ${siteId}`)
33
- const url = `https://api.netlify.com/api/v1/live_sessions?site_id=${siteId}`
34
34
 
35
+ const url = `https://api.netlify.com/api/v1/live_sessions?site_id=${siteId}&slug=${slug}`
35
36
  const response = await fetch(url, {
36
37
  method: 'POST',
37
38
  headers: {
@@ -87,10 +88,11 @@ const installTunnelClient = async function () {
87
88
  })
88
89
  }
89
90
 
90
- export const startLiveTunnel = async ({ localPort, netlifyApiToken, siteId }) => {
91
+ export const startLiveTunnel = async ({ localPort, netlifyApiToken, siteId, slug }) => {
91
92
  const session = await createTunnel({
92
93
  siteId,
93
94
  netlifyApiToken,
95
+ slug,
94
96
  })
95
97
 
96
98
  const isLiveTunnelReady = async function () {
@@ -121,3 +123,27 @@ export const startLiveTunnel = async ({ localPort, netlifyApiToken, siteId }) =>
121
123
 
122
124
  return session.session_url
123
125
  }
126
+
127
+ export const getLiveTunnelSlug = (state, override) => {
128
+ if (override !== undefined) {
129
+ return override
130
+ }
131
+
132
+ const newSlug = generateRandomSlug()
133
+
134
+ try {
135
+ const existingSlug = state.get(SLUG_LOCAL_STATE_KEY)
136
+
137
+ if (existingSlug !== undefined) {
138
+ return existingSlug
139
+ }
140
+
141
+ state.set(SLUG_LOCAL_STATE_KEY, newSlug)
142
+ } catch (error) {
143
+ log(`${NETLIFYDEVERR} Could not read or write local state file: ${error.message}`)
144
+ }
145
+
146
+ return newSlug
147
+ }
148
+
149
+ const generateRandomSlug = () => uuidv4().slice(0, 8)