netlify-cli 8.16.0 → 8.17.2
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 +1835 -155
- package/package.json +8 -8
- package/src/commands/dev/dev.js +34 -4
- package/src/commands/graph/graph-edit.js +3 -13
- package/src/functions-templates/javascript/stripe-charge/package-lock.json +7 -7
- package/src/functions-templates/javascript/stripe-subscription/package-lock.json +7 -7
- package/src/functions-templates/typescript/hello-world/package-lock.json +8 -8
- package/src/lib/exec-fetcher.js +2 -4
- package/src/lib/one-graph/cli-client.js +131 -12
- package/src/lib/one-graph/cli-netlify-graph.js +45 -1
- package/src/utils/proxy.js +4 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "netlify-cli",
|
|
3
3
|
"description": "Netlify command line tool",
|
|
4
|
-
"version": "8.
|
|
4
|
+
"version": "8.17.2",
|
|
5
5
|
"author": "Netlify Inc.",
|
|
6
6
|
"contributors": [
|
|
7
7
|
"Mathias Biilmann <matt@netlify.com> (https://twitter.com/biilmann)",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"test:dev:ava": "ava --verbose",
|
|
64
64
|
"test:ci:ava:unit": "c8 -r json ava --no-worker-threads tests/unit/**/*.test.js tools/**/*.test.js",
|
|
65
65
|
"test:ci:ava:integration": "c8 -r json ava --concurrency 1 --no-worker-threads tests/integration/**/*.test.js",
|
|
66
|
-
"test:affected": "node ./tools/affected-test.
|
|
66
|
+
"test:affected": "node ./tools/affected-test.mjs",
|
|
67
67
|
"e2e": "node ./tools/e2e/run.mjs",
|
|
68
68
|
"docs": "node ./site/scripts/docs.mjs",
|
|
69
69
|
"watch": "c8 --reporter=lcov ava --watch",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"postinstall": "node ./scripts/postinstall.js"
|
|
74
74
|
},
|
|
75
75
|
"config": {
|
|
76
|
-
"eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{src,
|
|
76
|
+
"eslint": "--ignore-path .gitignore --cache --format=codeframe --max-warnings=0 \"{src,scripts,site,tests,.github}/**/*.{mjs,cjs,js,md,html}\" \"*.{mjs,cjs,js,md,html}\" \".*.{mjs,cjs,js,md,html}\"",
|
|
77
77
|
"prettier": "--ignore-path .gitignore --loglevel=warn \"{src,tools,scripts,site,tests,.github}/**/*.{mjs,cjs,js,md,yml,json,html}\" \"*.{mjs,cjs,js,yml,json,html}\" \".*.{mjs,cjs,js,yml,json,html}\" \"!CHANGELOG.md\" \"!npm-shrinkwrap.json\" \"!.github/**/*.md\""
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"chokidar": "^3.0.2",
|
|
99
99
|
"ci-info": "^3.0.0",
|
|
100
100
|
"clean-deep": "^3.0.2",
|
|
101
|
-
"commander": "^
|
|
101
|
+
"commander": "^9.0.0",
|
|
102
102
|
"concordance": "^5.0.0",
|
|
103
103
|
"configstore": "^5.0.0",
|
|
104
104
|
"content-type": "^1.0.4",
|
|
@@ -184,11 +184,11 @@
|
|
|
184
184
|
"uuid": "^8.0.0",
|
|
185
185
|
"wait-port": "^0.2.2",
|
|
186
186
|
"winston": "^3.2.1",
|
|
187
|
-
"write-file-atomic": "^
|
|
187
|
+
"write-file-atomic": "^4.0.0"
|
|
188
188
|
},
|
|
189
189
|
"devDependencies": {
|
|
190
190
|
"@babel/preset-react": "^7.12.13",
|
|
191
|
-
"@netlify/eslint-config-node": "^
|
|
191
|
+
"@netlify/eslint-config-node": "^5.1.2",
|
|
192
192
|
"ava": "^4.0.0",
|
|
193
193
|
"c8": "^7.11.0",
|
|
194
194
|
"eslint-plugin-sort-destructure-keys": "^1.3.5",
|
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
"ini": "^2.0.0",
|
|
201
201
|
"mock-fs": "^5.1.2",
|
|
202
202
|
"p-timeout": "^4.0.0",
|
|
203
|
-
"
|
|
203
|
+
"rewiremock": "^3.14.3",
|
|
204
204
|
"seedrandom": "^3.0.5",
|
|
205
205
|
"serialize-javascript": "^6.0.0",
|
|
206
206
|
"sinon": "^13.0.0",
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
},
|
|
216
216
|
"ava": {
|
|
217
217
|
"files": [
|
|
218
|
-
"tools/**/*.test.
|
|
218
|
+
"tools/**/*.test.mjs",
|
|
219
219
|
"tests/**/*.test.js"
|
|
220
220
|
],
|
|
221
221
|
"cache": true,
|
package/src/commands/dev/dev.js
CHANGED
|
@@ -11,8 +11,18 @@ const stripAnsiCc = require('strip-ansi-control-characters')
|
|
|
11
11
|
const waitPort = require('wait-port')
|
|
12
12
|
|
|
13
13
|
const { startFunctionsServer } = require('../../lib/functions/server')
|
|
14
|
-
const {
|
|
15
|
-
|
|
14
|
+
const {
|
|
15
|
+
OneGraphCliClient,
|
|
16
|
+
loadCLISession,
|
|
17
|
+
persistNewOperationsDocForSession,
|
|
18
|
+
startOneGraphCLISession,
|
|
19
|
+
} = require('../../lib/one-graph/cli-client')
|
|
20
|
+
const {
|
|
21
|
+
defaultExampleOperationsDoc,
|
|
22
|
+
getGraphEditUrlBySiteId,
|
|
23
|
+
getNetlifyGraphConfig,
|
|
24
|
+
readGraphQLOperationsSourceFile,
|
|
25
|
+
} = require('../../lib/one-graph/cli-netlify-graph')
|
|
16
26
|
const {
|
|
17
27
|
NETLIFYDEV,
|
|
18
28
|
NETLIFYDEVERR,
|
|
@@ -349,9 +359,29 @@ const dev = async (options, command) => {
|
|
|
349
359
|
await OneGraphCliClient.ensureAppForSite(netlifyToken, site.id)
|
|
350
360
|
const netlifyGraphConfig = await getNetlifyGraphConfig({ command, options, settings })
|
|
351
361
|
|
|
352
|
-
|
|
362
|
+
let graphqlDocument = readGraphQLOperationsSourceFile(netlifyGraphConfig)
|
|
363
|
+
|
|
364
|
+
if (!graphqlDocument || graphqlDocument.trim().length === 0) {
|
|
365
|
+
graphqlDocument = defaultExampleOperationsDoc
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
await startOneGraphCLISession({ netlifyGraphConfig, netlifyToken, site, state })
|
|
369
|
+
|
|
370
|
+
// Should be created by startOneGraphCLISession
|
|
371
|
+
const oneGraphSessionId = loadCLISession(state)
|
|
353
372
|
|
|
354
|
-
|
|
373
|
+
await persistNewOperationsDocForSession({
|
|
374
|
+
netlifyToken,
|
|
375
|
+
oneGraphSessionId,
|
|
376
|
+
operationsDoc: graphqlDocument,
|
|
377
|
+
siteId: site.id,
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
const graphEditUrl = getGraphEditUrlBySiteId({ siteId: site.id, oneGraphSessionId })
|
|
381
|
+
|
|
382
|
+
log(
|
|
383
|
+
`Starting Netlify Graph session, to edit your library visit ${graphEditUrl} or run \`netlify graph:edit\` in another tab`,
|
|
384
|
+
)
|
|
355
385
|
}
|
|
356
386
|
|
|
357
387
|
printBanner({ url })
|
|
@@ -3,7 +3,7 @@ const gitRepoInfo = require('git-repo-info')
|
|
|
3
3
|
const { OneGraphCliClient, generateSessionName, loadCLISession } = require('../../lib/one-graph/cli-client')
|
|
4
4
|
const {
|
|
5
5
|
defaultExampleOperationsDoc,
|
|
6
|
-
|
|
6
|
+
getGraphEditUrlBySiteId,
|
|
7
7
|
getNetlifyGraphConfig,
|
|
8
8
|
readGraphQLOperationsSourceFile,
|
|
9
9
|
} = require('../../lib/one-graph/cli-netlify-graph')
|
|
@@ -19,7 +19,7 @@ const { createCLISession, createPersistedQuery, ensureAppForSite, updateCLISessi
|
|
|
19
19
|
* @returns
|
|
20
20
|
*/
|
|
21
21
|
const graphEdit = async (options, command) => {
|
|
22
|
-
const {
|
|
22
|
+
const { site, state } = command.netlify
|
|
23
23
|
const siteId = site.id
|
|
24
24
|
|
|
25
25
|
if (!site.id) {
|
|
@@ -60,17 +60,7 @@ const graphEdit = async (options, command) => {
|
|
|
60
60
|
|
|
61
61
|
await updateCLISessionMetadata(netlifyToken, siteId, oneGraphSessionId, { docId: persistedDoc.id })
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (!siteName) {
|
|
66
|
-
const siteData = await api.getSite({ siteId })
|
|
67
|
-
siteName = siteData.name
|
|
68
|
-
if (!siteName) {
|
|
69
|
-
error(`No site name found for siteId ${siteId}`)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const graphEditUrl = getGraphEditUrlBySiteName({ siteName, oneGraphSessionId })
|
|
63
|
+
const graphEditUrl = getGraphEditUrlBySiteId({ siteId, oneGraphSessionId })
|
|
74
64
|
|
|
75
65
|
await openBrowser({ url: graphEditUrl })
|
|
76
66
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"stripe": "^8.
|
|
12
|
+
"stripe": "^8.202.0"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"node_modules/@types/node": {
|
|
@@ -105,9 +105,9 @@
|
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
107
|
"node_modules/stripe": {
|
|
108
|
-
"version": "8.
|
|
109
|
-
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.
|
|
110
|
-
"integrity": "sha512-
|
|
108
|
+
"version": "8.202.0",
|
|
109
|
+
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.202.0.tgz",
|
|
110
|
+
"integrity": "sha512-3YGHVnUatEn/At5+aRy+REdB2IyVa96/zls2xvQrKFTgaJzRu1MsJcK0GKg0p2B0y0VqlZo9gmdDEqphSHHvtA==",
|
|
111
111
|
"dependencies": {
|
|
112
112
|
"@types/node": ">=8.1.0",
|
|
113
113
|
"qs": "^6.6.0"
|
|
@@ -184,9 +184,9 @@
|
|
|
184
184
|
}
|
|
185
185
|
},
|
|
186
186
|
"stripe": {
|
|
187
|
-
"version": "8.
|
|
188
|
-
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.
|
|
189
|
-
"integrity": "sha512-
|
|
187
|
+
"version": "8.202.0",
|
|
188
|
+
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.202.0.tgz",
|
|
189
|
+
"integrity": "sha512-3YGHVnUatEn/At5+aRy+REdB2IyVa96/zls2xvQrKFTgaJzRu1MsJcK0GKg0p2B0y0VqlZo9gmdDEqphSHHvtA==",
|
|
190
190
|
"requires": {
|
|
191
191
|
"@types/node": ">=8.1.0",
|
|
192
192
|
"qs": "^6.6.0"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"stripe": "^8.
|
|
12
|
+
"stripe": "^8.202.0"
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"node_modules/@types/node": {
|
|
@@ -105,9 +105,9 @@
|
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
107
|
"node_modules/stripe": {
|
|
108
|
-
"version": "8.
|
|
109
|
-
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.
|
|
110
|
-
"integrity": "sha512-
|
|
108
|
+
"version": "8.202.0",
|
|
109
|
+
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.202.0.tgz",
|
|
110
|
+
"integrity": "sha512-3YGHVnUatEn/At5+aRy+REdB2IyVa96/zls2xvQrKFTgaJzRu1MsJcK0GKg0p2B0y0VqlZo9gmdDEqphSHHvtA==",
|
|
111
111
|
"dependencies": {
|
|
112
112
|
"@types/node": ">=8.1.0",
|
|
113
113
|
"qs": "^6.6.0"
|
|
@@ -184,9 +184,9 @@
|
|
|
184
184
|
}
|
|
185
185
|
},
|
|
186
186
|
"stripe": {
|
|
187
|
-
"version": "8.
|
|
188
|
-
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.
|
|
189
|
-
"integrity": "sha512-
|
|
187
|
+
"version": "8.202.0",
|
|
188
|
+
"resolved": "https://registry.npmjs.org/stripe/-/stripe-8.202.0.tgz",
|
|
189
|
+
"integrity": "sha512-3YGHVnUatEn/At5+aRy+REdB2IyVa96/zls2xvQrKFTgaJzRu1MsJcK0GKg0p2B0y0VqlZo9gmdDEqphSHHvtA==",
|
|
190
190
|
"requires": {
|
|
191
191
|
"@types/node": ">=8.1.0",
|
|
192
192
|
"qs": "^6.6.0"
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@netlify/functions": "^0.11.0",
|
|
13
|
-
"@types/node": "^14.
|
|
14
|
-
"typescript": "^4.
|
|
13
|
+
"@types/node": "^14.18.10",
|
|
14
|
+
"typescript": "^4.0.0"
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"node_modules/@netlify/functions": {
|
|
@@ -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.10",
|
|
30
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.10.tgz",
|
|
31
|
+
"integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ=="
|
|
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.10",
|
|
62
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.10.tgz",
|
|
63
|
+
"integrity": "sha512-6iihJ/Pp5fsFJ/aEDGyvT4pHGmCpq7ToQ/yf4bl5SbVAvwpspYJ+v3jO7n8UyjhQVHTy+KNszOozDdv+O6sovQ=="
|
|
64
64
|
},
|
|
65
65
|
"is-promise": {
|
|
66
66
|
"version": "4.0.0",
|
package/src/lib/exec-fetcher.js
CHANGED
|
@@ -8,7 +8,7 @@ const terminalLink = require('terminal-link')
|
|
|
8
8
|
|
|
9
9
|
// cannot directly import from ../utils as it would create a circular dependency.
|
|
10
10
|
// the file `src/utils/live-tunnel.js` depends on this file
|
|
11
|
-
const { NETLIFYDEVWARN,
|
|
11
|
+
const { NETLIFYDEVWARN, error, log } = require('../utils/command-helpers')
|
|
12
12
|
const execa = require('../utils/execa')
|
|
13
13
|
|
|
14
14
|
const isWindows = () => process.platform === 'win32'
|
|
@@ -127,9 +127,7 @@ const fetchLatestVersion = async ({ destination, execName, extension, latestVers
|
|
|
127
127
|
|
|
128
128
|
const issueLink = terminalLink('Create a new CLI issue', createIssueLink.href)
|
|
129
129
|
|
|
130
|
-
error(`The operating system ${
|
|
131
|
-
arch,
|
|
132
|
-
)} is currently not supported!
|
|
130
|
+
error(`The operating system ${platform} with the CPU architecture ${arch} is currently not supported!
|
|
133
131
|
|
|
134
132
|
Please open up an issue on our CLI repository so that we can support it:
|
|
135
133
|
${issueLink}`)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
/* eslint-disable eslint-comments/disable-enable-pair */
|
|
2
2
|
/* eslint-disable fp/no-loops */
|
|
3
|
+
const crypto = require('crypto')
|
|
3
4
|
const os = require('os')
|
|
5
|
+
const path = require('path')
|
|
4
6
|
|
|
7
|
+
const gitRepoInfo = require('git-repo-info')
|
|
5
8
|
const { GraphQL, InternalConsole, OneGraphClient } = require('netlify-onegraph-internal')
|
|
6
9
|
const { NetlifyGraph } = require('netlify-onegraph-internal')
|
|
7
10
|
|
|
8
11
|
const { chalk, error, log, warn } = require('../../utils')
|
|
9
|
-
|
|
10
|
-
const { createCLISession, createPersistedQuery, ensureAppForSite, updateCLISessionMetadata } = OneGraphClient
|
|
12
|
+
const { watchDebounced } = require('../functions/watcher')
|
|
11
13
|
|
|
12
14
|
const {
|
|
13
15
|
generateFunctionsFile,
|
|
@@ -19,6 +21,7 @@ const {
|
|
|
19
21
|
|
|
20
22
|
const { parse } = GraphQL
|
|
21
23
|
const { defaultExampleOperationsDoc, extractFunctionsFromOperationDoc } = NetlifyGraph
|
|
24
|
+
const { createCLISession, createPersistedQuery, ensureAppForSite, updateCLISessionMetadata } = OneGraphClient
|
|
22
25
|
|
|
23
26
|
const internalConsole = {
|
|
24
27
|
log,
|
|
@@ -27,6 +30,9 @@ const internalConsole = {
|
|
|
27
30
|
debug: console.debug,
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
const witnessedIncomingDocumentHashes = []
|
|
34
|
+
|
|
35
|
+
// Keep track of which document hashes we've received from the server so we can ignore events from the filesystem based on them
|
|
30
36
|
InternalConsole.registerConsole(internalConsole)
|
|
31
37
|
|
|
32
38
|
/**
|
|
@@ -108,6 +114,26 @@ const monitorCLISessionEvents = (input) => {
|
|
|
108
114
|
return close
|
|
109
115
|
}
|
|
110
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Monitor the operations document for changes
|
|
119
|
+
* @param {object} input
|
|
120
|
+
* @param {NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events
|
|
121
|
+
* @param {function} input.onAdd A callback function to handle when the operations document is added
|
|
122
|
+
* @param {function} input.onChange A callback function to handle when the operations document is changed
|
|
123
|
+
* @param {function} input.onUnlink A callback function to handle when the operations document is unlinked
|
|
124
|
+
* @returns {Promise<watcher>}
|
|
125
|
+
*/
|
|
126
|
+
const monitorOperationFile = async ({ netlifyGraphConfig, onAdd, onChange, onUnlink }) => {
|
|
127
|
+
const filePath = path.resolve(...netlifyGraphConfig.graphQLOperationsSourceFilename)
|
|
128
|
+
const newWatcher = await watchDebounced([filePath], {
|
|
129
|
+
onAdd,
|
|
130
|
+
onChange,
|
|
131
|
+
onUnlink,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
return newWatcher
|
|
135
|
+
}
|
|
136
|
+
|
|
111
137
|
/**
|
|
112
138
|
* Fetch the schema for a site, and regenerate all of the downstream files
|
|
113
139
|
* @param {object} input
|
|
@@ -146,7 +172,44 @@ const refetchAndGenerateFromOneGraph = async (input) => {
|
|
|
146
172
|
}
|
|
147
173
|
|
|
148
174
|
/**
|
|
149
|
-
*
|
|
175
|
+
* Regenerate the function library based on the current operations document on disk
|
|
176
|
+
* @param {object} input
|
|
177
|
+
* @param {string} input.schema The GraphQL schema to use when generating code
|
|
178
|
+
* @param {NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events
|
|
179
|
+
* @returns
|
|
180
|
+
*/
|
|
181
|
+
const regenerateFunctionsFileFromOperationsFile = (input) => {
|
|
182
|
+
const { netlifyGraphConfig, schema } = input
|
|
183
|
+
|
|
184
|
+
const appOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig)
|
|
185
|
+
|
|
186
|
+
const hash = quickHash(appOperationsDoc)
|
|
187
|
+
|
|
188
|
+
if (witnessedIncomingDocumentHashes.includes(hash)) {
|
|
189
|
+
// We've already seen this document, so don't regenerate
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const parsedDoc = parse(appOperationsDoc, {
|
|
194
|
+
noLocation: true,
|
|
195
|
+
})
|
|
196
|
+
const { fragments, functions } = extractFunctionsFromOperationDoc(parsedDoc)
|
|
197
|
+
generateFunctionsFile({ netlifyGraphConfig, schema, operationsDoc: appOperationsDoc, functions, fragments })
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Compute a md5 hash of a string
|
|
202
|
+
* @param {string} input String to compute a quick md5 hash for
|
|
203
|
+
* @returns hex digest of the input string
|
|
204
|
+
*/
|
|
205
|
+
const quickHash = (input) => {
|
|
206
|
+
const hashSum = crypto.createHash('md5')
|
|
207
|
+
hashSum.update(input)
|
|
208
|
+
return hashSum.digest('hex')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Fetch a persisted operations doc by its id, write it to the system, and regenerate the library
|
|
150
213
|
* @param {object} input
|
|
151
214
|
* @param {string} input.siteId The site id to query against
|
|
152
215
|
* @param {string} input.netlifyToken The (typically netlify) access token that is used for authentication, if any
|
|
@@ -155,7 +218,7 @@ const refetchAndGenerateFromOneGraph = async (input) => {
|
|
|
155
218
|
* @param {NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events
|
|
156
219
|
* @returns
|
|
157
220
|
*/
|
|
158
|
-
const
|
|
221
|
+
const updateGraphQLOperationsFileFromPersistedDoc = async (input) => {
|
|
159
222
|
const { docId, netlifyGraphConfig, netlifyToken, schema, siteId } = input
|
|
160
223
|
const persistedDoc = await OneGraphClient.fetchPersistedQuery(netlifyToken, siteId, docId)
|
|
161
224
|
if (!persistedDoc) {
|
|
@@ -166,12 +229,17 @@ const updateGraphQLOperationsFile = async (input) => {
|
|
|
166
229
|
const doc = persistedDoc.query
|
|
167
230
|
|
|
168
231
|
writeGraphQLOperationsSourceFile(netlifyGraphConfig, doc)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
const
|
|
174
|
-
|
|
232
|
+
regenerateFunctionsFileFromOperationsFile({ netlifyGraphConfig, schema })
|
|
233
|
+
|
|
234
|
+
const hash = quickHash(doc)
|
|
235
|
+
|
|
236
|
+
const relevantHasLength = 10
|
|
237
|
+
|
|
238
|
+
if (witnessedIncomingDocumentHashes.length > relevantHasLength) {
|
|
239
|
+
witnessedIncomingDocumentHashes.shift()
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
witnessedIncomingDocumentHashes.push(hash)
|
|
175
243
|
}
|
|
176
244
|
|
|
177
245
|
const handleCliSessionEvent = async ({ event, netlifyGraphConfig, netlifyToken, schema, siteId }) => {
|
|
@@ -184,7 +252,13 @@ const handleCliSessionEvent = async ({ event, netlifyGraphConfig, netlifyToken,
|
|
|
184
252
|
await generateHandler(netlifyGraphConfig, schema, payload.operationId, payload)
|
|
185
253
|
break
|
|
186
254
|
case 'OneGraphNetlifyCliSessionPersistedLibraryUpdatedEvent':
|
|
187
|
-
await
|
|
255
|
+
await updateGraphQLOperationsFileFromPersistedDoc({
|
|
256
|
+
netlifyToken,
|
|
257
|
+
docId: payload.docId,
|
|
258
|
+
netlifyGraphConfig,
|
|
259
|
+
schema,
|
|
260
|
+
siteId,
|
|
261
|
+
})
|
|
188
262
|
break
|
|
189
263
|
default: {
|
|
190
264
|
warn(`Unrecognized event received, you may need to upgrade your CLI version`, __typename, payload)
|
|
@@ -193,6 +267,24 @@ const handleCliSessionEvent = async ({ event, netlifyGraphConfig, netlifyToken,
|
|
|
193
267
|
}
|
|
194
268
|
}
|
|
195
269
|
|
|
270
|
+
const persistNewOperationsDocForSession = async ({ netlifyToken, oneGraphSessionId, operationsDoc, siteId }) => {
|
|
271
|
+
const { branch } = gitRepoInfo()
|
|
272
|
+
|
|
273
|
+
const payload = {
|
|
274
|
+
appId: siteId,
|
|
275
|
+
description: 'Temporary snapshot of local queries',
|
|
276
|
+
document: operationsDoc,
|
|
277
|
+
tags: ['netlify-cli', `session:${oneGraphSessionId}`, `git-branch:${branch}`, `local-change`],
|
|
278
|
+
}
|
|
279
|
+
const persistedDoc = await createPersistedQuery(netlifyToken, payload)
|
|
280
|
+
const newMetadata = await { docId: persistedDoc.id }
|
|
281
|
+
const result = await OneGraphClient.updateCLISessionMetadata(netlifyToken, siteId, oneGraphSessionId, newMetadata)
|
|
282
|
+
|
|
283
|
+
if (result.errors) {
|
|
284
|
+
warn('Unable to update session metadata with updated operations doc', result.errors)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
196
288
|
/**
|
|
197
289
|
* Load the CLI session id from the local state
|
|
198
290
|
* @param {state} state
|
|
@@ -201,7 +293,7 @@ const handleCliSessionEvent = async ({ event, netlifyGraphConfig, netlifyToken,
|
|
|
201
293
|
const loadCLISession = (state) => state.get('oneGraphSessionId')
|
|
202
294
|
|
|
203
295
|
/**
|
|
204
|
-
* Idemponentially save the CLI session id to the local state and start monitoring for CLI events
|
|
296
|
+
* Idemponentially save the CLI session id to the local state and start monitoring for CLI events, upstream schema changes, and local operation file changes
|
|
205
297
|
* @param {object} input
|
|
206
298
|
* @param {string} input.netlifyToken The (typically netlify) access token that is used for authentication, if any
|
|
207
299
|
* @param {NetlifyGraphConfig} input.netlifyGraphConfig A standalone config object that contains all the information necessary for Netlify Graph to process events
|
|
@@ -223,6 +315,32 @@ const startOneGraphCLISession = async (input) => {
|
|
|
223
315
|
const enabledServices = []
|
|
224
316
|
const schema = await OneGraphClient.fetchOneGraphSchema(site.id, enabledServices)
|
|
225
317
|
|
|
318
|
+
monitorOperationFile({
|
|
319
|
+
netlifyGraphConfig,
|
|
320
|
+
onChange: async (filePath) => {
|
|
321
|
+
log('NetlifyGraph operation file changed at', filePath, 'updating function library...')
|
|
322
|
+
regenerateFunctionsFileFromOperationsFile({ netlifyGraphConfig, schema })
|
|
323
|
+
const newOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig)
|
|
324
|
+
await persistNewOperationsDocForSession({
|
|
325
|
+
netlifyToken,
|
|
326
|
+
oneGraphSessionId,
|
|
327
|
+
operationsDoc: newOperationsDoc,
|
|
328
|
+
siteId: site.id,
|
|
329
|
+
})
|
|
330
|
+
},
|
|
331
|
+
onAdd: async (filePath) => {
|
|
332
|
+
log('NetlifyGraph operation file created at', filePath, 'creating function library...')
|
|
333
|
+
regenerateFunctionsFileFromOperationsFile({ netlifyGraphConfig, schema })
|
|
334
|
+
const newOperationsDoc = readGraphQLOperationsSourceFile(netlifyGraphConfig)
|
|
335
|
+
await persistNewOperationsDocForSession({
|
|
336
|
+
netlifyToken,
|
|
337
|
+
oneGraphSessionId,
|
|
338
|
+
operationsDoc: newOperationsDoc,
|
|
339
|
+
siteId: site.id,
|
|
340
|
+
})
|
|
341
|
+
},
|
|
342
|
+
})
|
|
343
|
+
|
|
226
344
|
monitorCLISessionEvents({
|
|
227
345
|
appId: site.id,
|
|
228
346
|
netlifyToken,
|
|
@@ -273,6 +391,7 @@ module.exports = {
|
|
|
273
391
|
generateSessionName,
|
|
274
392
|
loadCLISession,
|
|
275
393
|
monitorCLISessionEvents,
|
|
394
|
+
persistNewOperationsDocForSession,
|
|
276
395
|
refetchAndGenerateFromOneGraph,
|
|
277
396
|
startOneGraphCLISession,
|
|
278
397
|
}
|
|
@@ -4,7 +4,7 @@ const process = require('process')
|
|
|
4
4
|
|
|
5
5
|
const { GraphQL, InternalConsole, NetlifyGraph } = require('netlify-onegraph-internal')
|
|
6
6
|
|
|
7
|
-
const { detectServerSettings, error, getFunctionsDir, log, warn } = require('../../utils')
|
|
7
|
+
const { detectServerSettings, error, execa, getFunctionsDir, log, warn } = require('../../utils')
|
|
8
8
|
|
|
9
9
|
const { printSchema } = GraphQL
|
|
10
10
|
|
|
@@ -241,6 +241,31 @@ const ensureFunctionsPath = (netlifyGraphConfig) => {
|
|
|
241
241
|
fs.mkdirSync(fullPath, { recursive: true })
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
let disablePrettierDueToPreviousError = false
|
|
245
|
+
|
|
246
|
+
const runPrettier = async (filePath) => {
|
|
247
|
+
if (disablePrettierDueToPreviousError) {
|
|
248
|
+
return
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const command = `prettier --write ${filePath}`
|
|
252
|
+
try {
|
|
253
|
+
const commandProcess = execa.command(command, {
|
|
254
|
+
preferLocal: true,
|
|
255
|
+
// windowsHide needs to be false for child process to terminate properly on Windows
|
|
256
|
+
windowsHide: false,
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
await commandProcess
|
|
260
|
+
} catch (prettierError) {
|
|
261
|
+
if (!disablePrettierDueToPreviousError) {
|
|
262
|
+
disablePrettierDueToPreviousError = true
|
|
263
|
+
warn(prettierError)
|
|
264
|
+
warn("Error while running prettier, make sure you have installed it globally with 'npm i -g prettier'")
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
244
269
|
/**
|
|
245
270
|
* Generate a library file with type definitions for a given NetlifyGraphConfig, operationsDoc, and schema, writing them to the filesystem
|
|
246
271
|
* @param {object} context
|
|
@@ -267,6 +292,8 @@ const generateFunctionsFile = ({ fragments, functions, netlifyGraphConfig, opera
|
|
|
267
292
|
typeDefinitionsSource,
|
|
268
293
|
'utf8',
|
|
269
294
|
)
|
|
295
|
+
runPrettier(path.resolve(...netlifyGraphConfig.netlifyGraphImplementationFilename))
|
|
296
|
+
runPrettier(path.resolve(...netlifyGraphConfig.netlifyGraphTypeDefinitionsFilename))
|
|
270
297
|
}
|
|
271
298
|
|
|
272
299
|
/**
|
|
@@ -384,6 +411,7 @@ const generateHandler = (netlifyGraphConfig, schema, operationId, handlerOptions
|
|
|
384
411
|
const absoluteFilename = path.resolve(...filenameArr)
|
|
385
412
|
|
|
386
413
|
fs.writeFileSync(absoluteFilename, content)
|
|
414
|
+
runPrettier(absoluteFilename)
|
|
387
415
|
})
|
|
388
416
|
}
|
|
389
417
|
|
|
@@ -405,6 +433,21 @@ const getGraphEditUrlBySiteName = ({ oneGraphSessionId, siteName }) => {
|
|
|
405
433
|
return url
|
|
406
434
|
}
|
|
407
435
|
|
|
436
|
+
/**
|
|
437
|
+
* Get a url to the Netlify Graph UI for the current session by a site's id
|
|
438
|
+
* @param {object} options
|
|
439
|
+
* @param {string} options.siteId The name of the site as used in the Netlify UI url scheme
|
|
440
|
+
* @param {string} options.oneGraphSessionId The oneGraph session id to use when generating the graph-edit link
|
|
441
|
+
* @returns {string} The url to the Netlify Graph UI for the current session
|
|
442
|
+
*/
|
|
443
|
+
const getGraphEditUrlBySiteId = ({ oneGraphSessionId, siteId }) => {
|
|
444
|
+
const host = process.env.NETLIFY_APP_HOST || 'app.netlify.com'
|
|
445
|
+
// http because app.netlify.com will redirect to https, and localhost will still work for development
|
|
446
|
+
const url = `http://${host}/site-redirect/${siteId}/graph/explorer?cliSessionId=${oneGraphSessionId}`
|
|
447
|
+
|
|
448
|
+
return url
|
|
449
|
+
}
|
|
450
|
+
|
|
408
451
|
module.exports = {
|
|
409
452
|
buildSchema,
|
|
410
453
|
defaultExampleOperationsDoc: NetlifyGraph.defaultExampleOperationsDoc,
|
|
@@ -413,6 +456,7 @@ module.exports = {
|
|
|
413
456
|
generateFunctionsFile,
|
|
414
457
|
generateHandlerSource: NetlifyGraph.generateHandlerSource,
|
|
415
458
|
generateHandler,
|
|
459
|
+
getGraphEditUrlBySiteId,
|
|
416
460
|
getGraphEditUrlBySiteName,
|
|
417
461
|
getNetlifyGraphConfig,
|
|
418
462
|
parse,
|
package/src/utils/proxy.js
CHANGED
|
@@ -213,6 +213,10 @@ const serveRedirect = async function ({ match, options, proxy, req, res }) {
|
|
|
213
213
|
|
|
214
214
|
const destURL = stripOrigin(dest)
|
|
215
215
|
|
|
216
|
+
if (isExternal(match)) {
|
|
217
|
+
return proxyToExternalUrl({ req, res, dest, destURL })
|
|
218
|
+
}
|
|
219
|
+
|
|
216
220
|
if (isRedirect(match)) {
|
|
217
221
|
res.writeHead(match.status, {
|
|
218
222
|
Location: destURL,
|
|
@@ -222,10 +226,6 @@ const serveRedirect = async function ({ match, options, proxy, req, res }) {
|
|
|
222
226
|
return
|
|
223
227
|
}
|
|
224
228
|
|
|
225
|
-
if (isExternal(match)) {
|
|
226
|
-
return proxyToExternalUrl({ req, res, dest, destURL })
|
|
227
|
-
}
|
|
228
|
-
|
|
229
229
|
const ct = req.headers['content-type'] ? contentType.parse(req).type : ''
|
|
230
230
|
if (
|
|
231
231
|
req.method === 'POST' &&
|