netlify-cli 10.13.0 → 10.14.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.
@@ -24,13 +24,6 @@ const {
24
24
 
25
25
  const { parse } = GraphQL
26
26
  const { defaultExampleOperationsDoc, extractFunctionsFromOperationDoc } = NetlifyGraph
27
- const {
28
- ensureAppForSite,
29
- executeCreatePersistedQueryMutation,
30
- executeMarkCliSessionActiveHeartbeat,
31
- executeMarkCliSessionInactive,
32
- updateCLISessionMetadata,
33
- } = OneGraphClient
34
27
 
35
28
  const internalConsole = {
36
29
  log,
@@ -71,18 +64,25 @@ const monitorCLISessionEvents = (input) => {
71
64
  let nextMarkActiveHeartbeat = defaultHeartbeatFrequency
72
65
 
73
66
  const markActiveHelper = async () => {
74
- const graphJwt = await OneGraphClient.getGraphJwtForSite({ siteId: appId, nfToken: netlifyToken })
75
- const fullSession = await OneGraphClient.fetchCliSession({ jwt: graphJwt.jwt, appId, sessionId: currentSessionId })
76
- // @ts-ignore
77
- const heartbeatIntervalms = fullSession.session.cliHeartbeatIntervalMs || defaultHeartbeatFrequency
78
- nextMarkActiveHeartbeat = heartbeatIntervalms
79
- const markCLISessionActiveResult = await executeMarkCliSessionActiveHeartbeat(
80
- graphJwt.jwt,
81
- site.id,
82
- currentSessionId,
83
- )
84
- if (markCLISessionActiveResult.errors && markCLISessionActiveResult.errors.length !== 0) {
85
- warn(`Failed to mark CLI session active: ${markCLISessionActiveResult.errors.join(', ')}`)
67
+ try {
68
+ const graphJwt = await OneGraphClient.getGraphJwtForSite({ siteId: appId, nfToken: netlifyToken })
69
+ const fullSession = await OneGraphClient.fetchCliSession({
70
+ jwt: graphJwt.jwt,
71
+ appId,
72
+ sessionId: currentSessionId,
73
+ })
74
+ const heartbeatIntervalms = fullSession.session.cliHeartbeatIntervalMs || defaultHeartbeatFrequency
75
+ nextMarkActiveHeartbeat = heartbeatIntervalms
76
+ const markCLISessionActiveResult = await OneGraphClient.executeMarkCliSessionActiveHeartbeat(
77
+ graphJwt.jwt,
78
+ site.id,
79
+ currentSessionId,
80
+ )
81
+ if (markCLISessionActiveResult.errors && markCLISessionActiveResult.errors.length !== 0) {
82
+ warn(`Failed to mark CLI session active: ${markCLISessionActiveResult.errors.join(', ')}`)
83
+ }
84
+ } catch {
85
+ warn(`Unable to reach Netlify Graph servers in order to mark CLI session active`)
86
86
  }
87
87
  setTimeout(markActiveHelper, nextMarkActiveHeartbeat)
88
88
  }
@@ -92,23 +92,27 @@ const monitorCLISessionEvents = (input) => {
92
92
  const enabledServiceWatcher = async (jwt, { appId: siteId, sessionId }) => {
93
93
  const enabledServices = state.get('oneGraphEnabledServices') || ['onegraph']
94
94
 
95
- const enabledServicesInfo = await OneGraphClient.fetchEnabledServicesForSession(jwt, siteId, sessionId)
96
- if (!enabledServicesInfo) {
97
- warn('Unable to fetch enabled services for site for code generation')
98
- return
99
- }
100
- const newEnabledServices = enabledServicesInfo.map((service) => service.graphQLField)
101
- const enabledServicesCompareKey = enabledServices.sort().join(',')
102
- const newEnabledServicesCompareKey = newEnabledServices.sort().join(',')
103
-
104
- if (enabledServicesCompareKey !== newEnabledServicesCompareKey) {
105
- log(
106
- `${chalk.magenta(
107
- 'Reloading',
108
- )} Netlify Graph schema..., ${enabledServicesCompareKey} => ${newEnabledServicesCompareKey}`,
109
- )
110
- await refetchAndGenerateFromOneGraph({ netlifyGraphConfig, state, jwt, siteId, sessionId })
111
- log(`${chalk.green('Reloaded')} Netlify Graph schema and regenerated functions`)
95
+ try {
96
+ const enabledServicesInfo = await OneGraphClient.fetchEnabledServicesForSession(jwt, siteId, sessionId)
97
+ if (!enabledServicesInfo) {
98
+ warn('Unable to fetch enabled services for site for code generation')
99
+ return
100
+ }
101
+ const newEnabledServices = enabledServicesInfo.map((service) => service.graphQLField)
102
+ const enabledServicesCompareKey = enabledServices.sort().join(',')
103
+ const newEnabledServicesCompareKey = newEnabledServices.sort().join(',')
104
+
105
+ if (enabledServicesCompareKey !== newEnabledServicesCompareKey) {
106
+ log(
107
+ `${chalk.magenta(
108
+ 'Reloading',
109
+ )} Netlify Graph schema..., ${enabledServicesCompareKey} => ${newEnabledServicesCompareKey}`,
110
+ )
111
+ await refetchAndGenerateFromOneGraph({ netlifyGraphConfig, state, jwt, siteId, sessionId })
112
+ log(`${chalk.green('Reloaded')} Netlify Graph schema and regenerated functions`)
113
+ }
114
+ } catch {
115
+ warn(`Unable to reach Netlify Graph servers in order to fetch enabled Graph services`)
112
116
  }
113
117
  }
114
118
 
@@ -119,39 +123,43 @@ const monitorCLISessionEvents = (input) => {
119
123
  let handle
120
124
 
121
125
  const helper = async () => {
122
- if (shouldClose) {
123
- clearTimeout(handle)
124
- onClose && onClose()
125
- }
126
-
127
- const graphJwt = await OneGraphClient.getGraphJwtForSite({ siteId: appId, nfToken: netlifyToken })
128
- const next = await OneGraphClient.fetchCliSessionEvents({ appId, jwt: graphJwt.jwt, sessionId: currentSessionId })
126
+ try {
127
+ if (shouldClose) {
128
+ clearTimeout(handle)
129
+ onClose && onClose()
130
+ }
129
131
 
130
- if (next && next.errors) {
131
- next.errors.forEach((fetchEventError) => {
132
- onError(fetchEventError)
133
- })
134
- }
132
+ const graphJwt = await OneGraphClient.getGraphJwtForSite({ siteId: appId, nfToken: netlifyToken })
133
+ const next = await OneGraphClient.fetchCliSessionEvents({ appId, jwt: graphJwt.jwt, sessionId: currentSessionId })
135
134
 
136
- const events = (next && next.events) || []
137
-
138
- if (events.length !== 0) {
139
- let ackIds = []
140
- try {
141
- ackIds = await onEvents(events)
142
- } catch (eventHandlerError) {
143
- warn(`Error handling event: ${eventHandlerError}`)
144
- } finally {
145
- await OneGraphClient.ackCLISessionEvents({
146
- appId,
147
- jwt: graphJwt.jwt,
148
- sessionId: currentSessionId,
149
- eventIds: ackIds,
135
+ if (next && next.errors) {
136
+ next.errors.forEach((fetchEventError) => {
137
+ onError(fetchEventError)
150
138
  })
151
139
  }
152
- }
153
140
 
154
- await enabledServiceWatcher(graphJwt.jwt, { appId, sessionId: currentSessionId })
141
+ const events = (next && next.events) || []
142
+
143
+ if (events.length !== 0) {
144
+ let ackIds = []
145
+ try {
146
+ ackIds = await onEvents(events)
147
+ } catch (eventHandlerError) {
148
+ warn(`Error handling event: ${eventHandlerError}`)
149
+ } finally {
150
+ await OneGraphClient.ackCLISessionEvents({
151
+ appId,
152
+ jwt: graphJwt.jwt,
153
+ sessionId: currentSessionId,
154
+ eventIds: ackIds,
155
+ })
156
+ }
157
+ }
158
+
159
+ await enabledServiceWatcher(graphJwt.jwt, { appId, sessionId: currentSessionId })
160
+ } catch {
161
+ warn(`Unable to reach Netlify Graph servers in order to sync Graph session`)
162
+ }
155
163
 
156
164
  handle = setTimeout(helper, frequency)
157
165
  }
@@ -172,7 +180,11 @@ const monitorCLISessionEvents = (input) => {
172
180
  * @returns {Promise<any>}
173
181
  */
174
182
  const monitorOperationFile = async ({ netlifyGraphConfig, onAdd, onChange, onUnlink }) => {
175
- const filePath = path.resolve(...netlifyGraphConfig.graphQLOperationsSourceFilename)
183
+ if (!netlifyGraphConfig.graphQLOperationsSourceFilename) {
184
+ error('Please configure `graphQLOperationsSourceFilename` in your `netlify.toml` [graph] section')
185
+ }
186
+
187
+ const filePath = path.resolve(...(netlifyGraphConfig.graphQLOperationsSourceFilename || []))
176
188
  const newWatcher = await watchDebounced([filePath], {
177
189
  depth: 1,
178
190
  onAdd,
@@ -211,6 +223,10 @@ const refetchAndGenerateFromOneGraph = async (input) => {
211
223
 
212
224
  const schema = await OneGraphClient.fetchOneGraphSchemaForServices(siteId, enabledServices)
213
225
 
226
+ if (!schema) {
227
+ error('Unable to fetch schema from Netlify Graph')
228
+ }
229
+
214
230
  let currentOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig)
215
231
  if (currentOperationsDoc.trim().length === 0) {
216
232
  currentOperationsDoc = defaultExampleOperationsDoc
@@ -280,29 +296,33 @@ const quickHash = (input) => {
280
296
  * @returns
281
297
  */
282
298
  const updateGraphQLOperationsFileFromPersistedDoc = async (input) => {
283
- const { docId, logger, netlifyGraphConfig, netlifyToken, schema, siteId } = input
284
- const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken })
285
- const persistedDoc = await OneGraphClient.fetchPersistedQuery(jwt, siteId, docId)
286
- if (!persistedDoc) {
287
- warn(`No persisted doc found for: ${docId}`)
288
- return
289
- }
299
+ try {
300
+ const { docId, logger, netlifyGraphConfig, netlifyToken, schema, siteId } = input
301
+ const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken })
302
+ const persistedDoc = await OneGraphClient.fetchPersistedQuery(jwt, siteId, docId)
303
+ if (!persistedDoc) {
304
+ warn(`No persisted doc found for: ${docId}`)
305
+ return
306
+ }
290
307
 
291
- // Sorts the operations stably, prepends the @netlify directive, etc.
292
- const operationsDocString = normalizeOperationsDoc(persistedDoc.query)
308
+ // Sorts the operations stably, prepends the @netlify directive, etc.
309
+ const operationsDocString = normalizeOperationsDoc(persistedDoc.query)
293
310
 
294
- writeGraphQLOperationsSourceFile({ logger, netlifyGraphConfig, operationsDocString })
295
- regenerateFunctionsFileFromOperationsFile({ netlifyGraphConfig, schema })
311
+ writeGraphQLOperationsSourceFile({ logger, netlifyGraphConfig, operationsDocString })
312
+ regenerateFunctionsFileFromOperationsFile({ netlifyGraphConfig, schema })
296
313
 
297
- const hash = quickHash(operationsDocString)
314
+ const hash = quickHash(operationsDocString)
298
315
 
299
- const relevantHasLength = 10
316
+ const relevantHasLength = 10
300
317
 
301
- if (witnessedIncomingDocumentHashes.length > relevantHasLength) {
302
- witnessedIncomingDocumentHashes.shift()
303
- }
318
+ if (witnessedIncomingDocumentHashes.length > relevantHasLength) {
319
+ witnessedIncomingDocumentHashes.shift()
320
+ }
304
321
 
305
- witnessedIncomingDocumentHashes.push(hash)
322
+ witnessedIncomingDocumentHashes.push(hash)
323
+ } catch {
324
+ warn(`Unable to reach Netlify Graph servers in order to update Graph operations file`)
325
+ }
306
326
  }
307
327
 
308
328
  const handleCliSessionEvent = async ({
@@ -357,15 +377,19 @@ const handleCliSessionEvent = async ({
357
377
  },
358
378
  }
359
379
 
360
- const graphJwt = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken })
361
-
362
- await OneGraphClient.executeCreateCLISessionEventMutation(
363
- {
364
- sessionId,
365
- payload: fileWrittenEvent,
366
- },
367
- { accesToken: graphJwt.jwt },
368
- )
380
+ try {
381
+ const graphJwt = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken })
382
+
383
+ await OneGraphClient.executeCreateCLISessionEventMutation(
384
+ {
385
+ sessionId,
386
+ payload: fileWrittenEvent,
387
+ },
388
+ { accessToken: graphJwt.jwt },
389
+ )
390
+ } catch {
391
+ warn(`Unable to reach Netlify Graph servers in order to notify handler files written to disk`)
392
+ }
369
393
  }
370
394
  break
371
395
  }
@@ -500,9 +524,23 @@ const persistNewOperationsDocForSession = async ({
500
524
  siteId,
501
525
  siteRoot,
502
526
  }) => {
527
+ try {
528
+ GraphQL.parse(operationsDoc)
529
+ } catch (parseError) {
530
+ // TODO: We should send a message to the web UI that the current GraphQL operations file can't be sync because it's invalid
531
+ warn(
532
+ `Unable to sync Graph operations file. Please ensure that your GraphQL operations file is valid GraphQL. Found error: ${JSON.stringify(
533
+ parseError,
534
+ null,
535
+ 2,
536
+ )}`,
537
+ )
538
+ return
539
+ }
540
+
503
541
  const { branch } = gitRepoInfo()
504
542
  const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken })
505
- const persistedResult = await executeCreatePersistedQueryMutation(
543
+ const persistedResult = await OneGraphClient.executeCreatePersistedQueryMutation(
506
544
  {
507
545
  appId: siteId,
508
546
  description: 'Temporary snapshot of local queries',
@@ -663,7 +701,7 @@ const startOneGraphCLISession = async (input) => {
663
701
  */
664
702
  const markCliSessionInactive = async ({ netlifyToken, sessionId, siteId }) => {
665
703
  const { jwt } = await OneGraphClient.getGraphJwtForSite({ siteId, nfToken: netlifyToken })
666
- const result = await executeMarkCliSessionInactive(jwt, siteId, sessionId)
704
+ const result = await OneGraphClient.executeMarkCliSessionInactive(jwt, siteId, sessionId)
667
705
  if (result.errors) {
668
706
  warn(`Unable to mark CLI session ${sessionId} inactive: ${JSON.stringify(result.errors, null, 2)}`)
669
707
  }
@@ -733,7 +771,7 @@ const ensureCLISession = async (input) => {
733
771
  }
734
772
 
735
773
  state.set('oneGraphSessionId', oneGraphSessionId)
736
- const { errors: markCLISessionActiveErrors } = await executeMarkCliSessionActiveHeartbeat(
774
+ const { errors: markCLISessionActiveErrors } = await OneGraphClient.executeMarkCliSessionActiveHeartbeat(
737
775
  jwt,
738
776
  site.id,
739
777
  oneGraphSessionId,
@@ -751,8 +789,8 @@ const OneGraphCliClient = {
751
789
  executeCreatePersistedQueryMutation: OneGraphClient.executeCreatePersistedQueryMutation,
752
790
  executeCreateApiTokenMutation: OneGraphClient.executeCreateApiTokenMutation,
753
791
  fetchCliSessionEvents: OneGraphClient.fetchCliSessionEvents,
754
- ensureAppForSite,
755
- updateCLISessionMetadata,
792
+ ensureAppForSite: OneGraphClient.ensureAppForSite,
793
+ updateCLISessionMetadata: OneGraphClient.updateCLISessionMetadata,
756
794
  getGraphJwtForSite: OneGraphClient.getGraphJwtForSite,
757
795
  }
758
796
 
@@ -20,7 +20,7 @@ const loadDotEnvFiles = async function ({ envFiles, projectDir }) {
20
20
  }
21
21
 
22
22
  // in the user configuration, the order is highest to lowest
23
- const defaultEnvFiles = ['.env.development', '.env']
23
+ const defaultEnvFiles = ['.env.development.local', '.env.local', '.env.development', '.env']
24
24
 
25
25
  const tryLoadDotEnvFiles = async ({ projectDir, dotenvFiles = defaultEnvFiles }) => {
26
26
  const results = await Promise.all(
@@ -199,7 +199,7 @@ const getNetlifyToml = ({
199
199
  # port = 3000 # Port that the dev server will be listening on
200
200
  # publish = "dist" # Folder with the static content for _redirect file
201
201
 
202
- ## more info on configuring this file: https://www.netlify.com/docs/netlify-toml-reference/
202
+ ## more info on configuring this file: https://docs.netlify.com/configure-builds/file-based-configuration/
203
203
  `
204
204
 
205
205
  const saveNetlifyToml = async ({ baseDir, buildCmd, buildDir, config, configPath, functionsDir, repositoryRoot }) => {