@gadgetinc/ggt 0.4.10 → 1.0.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/README.md +165 -93
- package/lib/__generated__/graphql.js +66 -1
- package/lib/__generated__/graphql.js.map +1 -1
- package/lib/commands/deploy.js +328 -230
- package/lib/commands/deploy.js.map +1 -1
- package/lib/commands/dev.js +445 -0
- package/lib/commands/dev.js.map +1 -0
- package/lib/commands/list.js +27 -19
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/login.js +15 -11
- package/lib/commands/login.js.map +1 -1
- package/lib/commands/logout.js +5 -5
- package/lib/commands/logout.js.map +1 -1
- package/lib/commands/open.js +200 -0
- package/lib/commands/open.js.map +1 -0
- package/lib/commands/pull.js +128 -0
- package/lib/commands/pull.js.map +1 -0
- package/lib/commands/push.js +126 -0
- package/lib/commands/push.js.map +1 -0
- package/lib/commands/root.js +46 -28
- package/lib/commands/root.js.map +1 -1
- package/lib/commands/status.js +61 -0
- package/lib/commands/status.js.map +1 -0
- package/lib/commands/version.js +6 -6
- package/lib/commands/version.js.map +1 -1
- package/lib/commands/whoami.js +6 -6
- package/lib/commands/whoami.js.map +1 -1
- package/lib/ggt.js +33 -8
- package/lib/ggt.js.map +1 -1
- package/lib/main.js +5 -0
- package/lib/main.js.map +1 -0
- package/lib/services/app/api/api.js +191 -0
- package/lib/services/app/api/api.js.map +1 -0
- package/lib/services/app/api/operation.js +12 -0
- package/lib/services/app/api/operation.js.map +1 -0
- package/lib/services/app/app.js +44 -10
- package/lib/services/app/app.js.map +1 -1
- package/lib/services/app/{edit/client.js → client.js} +29 -19
- package/lib/services/app/client.js.map +1 -0
- package/lib/services/app/edit/edit.js +67 -31
- package/lib/services/app/edit/edit.js.map +1 -1
- package/lib/services/app/edit/operation.js +4 -3
- package/lib/services/app/edit/operation.js.map +1 -1
- package/lib/services/app/{edit/error.js → error.js} +6 -6
- package/lib/services/app/error.js.map +1 -0
- package/lib/services/command/arg.js +4 -4
- package/lib/services/command/arg.js.map +1 -1
- package/lib/services/command/command.js +9 -7
- package/lib/services/command/command.js.map +1 -1
- package/lib/services/command/context.js +82 -20
- package/lib/services/command/context.js.map +1 -1
- package/lib/services/config/config.js +4 -7
- package/lib/services/config/config.js.map +1 -1
- package/lib/services/config/env.js +1 -1
- package/lib/services/config/env.js.map +1 -1
- package/lib/services/filesync/changes.js +76 -37
- package/lib/services/filesync/changes.js.map +1 -1
- package/lib/services/filesync/conflicts.js +10 -9
- package/lib/services/filesync/conflicts.js.map +1 -1
- package/lib/services/filesync/directory.js +16 -1
- package/lib/services/filesync/directory.js.map +1 -1
- package/lib/services/filesync/error.js +96 -27
- package/lib/services/filesync/error.js.map +1 -1
- package/lib/services/filesync/filesync.js +448 -490
- package/lib/services/filesync/filesync.js.map +1 -1
- package/lib/services/filesync/hashes.js +8 -5
- package/lib/services/filesync/hashes.js.map +1 -1
- package/lib/services/filesync/strategy.js +59 -0
- package/lib/services/filesync/strategy.js.map +1 -0
- package/lib/services/filesync/sync-json.js +475 -0
- package/lib/services/filesync/sync-json.js.map +1 -0
- package/lib/services/http/auth.js +30 -1
- package/lib/services/http/auth.js.map +1 -1
- package/lib/services/http/http.js +5 -0
- package/lib/services/http/http.js.map +1 -1
- package/lib/services/output/confirm.js +149 -0
- package/lib/services/output/confirm.js.map +1 -0
- package/lib/services/output/footer.js +22 -0
- package/lib/services/output/footer.js.map +1 -0
- package/lib/services/output/log/format/pretty.js +2 -1
- package/lib/services/output/log/format/pretty.js.map +1 -1
- package/lib/services/output/log/logger.js +13 -5
- package/lib/services/output/log/logger.js.map +1 -1
- package/lib/services/output/log/structured.js +2 -2
- package/lib/services/output/log/structured.js.map +1 -1
- package/lib/services/output/output.js +197 -0
- package/lib/services/output/output.js.map +1 -0
- package/lib/services/output/print.js +31 -0
- package/lib/services/output/print.js.map +1 -0
- package/lib/services/output/problems.js +84 -0
- package/lib/services/output/problems.js.map +1 -0
- package/lib/services/output/prompt.js +173 -40
- package/lib/services/output/prompt.js.map +1 -1
- package/lib/services/output/report.js +63 -19
- package/lib/services/output/report.js.map +1 -1
- package/lib/services/output/select.js +198 -0
- package/lib/services/output/select.js.map +1 -0
- package/lib/services/output/spinner.js +141 -0
- package/lib/services/output/spinner.js.map +1 -0
- package/lib/services/output/sprint.js +38 -15
- package/lib/services/output/sprint.js.map +1 -1
- package/lib/services/output/symbols.js +23 -0
- package/lib/services/output/symbols.js.map +1 -0
- package/lib/services/output/table.js +98 -0
- package/lib/services/output/table.js.map +1 -0
- package/lib/services/output/timestamp.js +12 -0
- package/lib/services/output/timestamp.js.map +1 -0
- package/lib/services/output/update.js +29 -9
- package/lib/services/output/update.js.map +1 -1
- package/lib/services/user/session.js +4 -0
- package/lib/services/user/session.js.map +1 -1
- package/lib/services/user/user.js +15 -10
- package/lib/services/user/user.js.map +1 -1
- package/lib/services/util/assert.js +11 -0
- package/lib/services/util/assert.js.map +1 -0
- package/lib/services/util/boolean.js +2 -2
- package/lib/services/util/boolean.js.map +1 -1
- package/lib/services/util/function.js +45 -7
- package/lib/services/util/function.js.map +1 -1
- package/lib/services/util/is.js +23 -2
- package/lib/services/util/is.js.map +1 -1
- package/lib/services/util/json.js +16 -13
- package/lib/services/util/json.js.map +1 -1
- package/lib/services/util/object.js +2 -2
- package/lib/services/util/object.js.map +1 -1
- package/lib/services/util/promise.js +5 -2
- package/lib/services/util/promise.js.map +1 -1
- package/lib/services/util/types.js.map +1 -1
- package/npm-shrinkwrap.json +3415 -2973
- package/package.json +47 -40
- package/bin/dev.cmd +0 -3
- package/bin/dev.js +0 -14
- package/bin/run.cmd +0 -3
- package/bin/run.js +0 -5
- package/lib/commands/sync.js +0 -284
- package/lib/commands/sync.js.map +0 -1
- package/lib/services/app/edit/client.js.map +0 -1
- package/lib/services/app/edit/error.js.map +0 -1
- package/lib/services/output/log/printer.js +0 -120
- package/lib/services/output/log/printer.js.map +0 -1
- package/lib/services/output/stream.js +0 -54
- package/lib/services/output/stream.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gadgetinc/ggt",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "The command-line interface for Gadget",
|
|
5
5
|
"homepage": "https://github.com/gadget-inc/ggt",
|
|
6
6
|
"bugs": {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"name": "Gadget Authors"
|
|
16
16
|
},
|
|
17
17
|
"type": "module",
|
|
18
|
-
"main": "lib/
|
|
18
|
+
"main": "lib/ggt.js",
|
|
19
19
|
"bin": {
|
|
20
|
-
"ggt": "
|
|
20
|
+
"ggt": "lib/main.js"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"/assets",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"/README.md"
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
|
-
"build": "npm run clean && swc src -d lib",
|
|
31
|
+
"build": "npm run clean && swc src -d lib --strip-leading-paths",
|
|
32
32
|
"changeset": "changeset",
|
|
33
33
|
"clean": "rimraf lib tmp/spec",
|
|
34
34
|
"dev": "vitest --watch",
|
|
@@ -44,41 +44,50 @@
|
|
|
44
44
|
"version": "changeset version && npm install --package-lock-only && ./scripts/generate-readme.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@sentry/node": "^7.
|
|
48
|
-
"@swc/helpers": "^0.5.
|
|
47
|
+
"@sentry/node": "^7.106.0",
|
|
48
|
+
"@swc/helpers": "^0.5.6",
|
|
49
|
+
"ansi-escapes": "^6.2.0",
|
|
49
50
|
"arg": "^5.0.2",
|
|
50
51
|
"boxen": "^7.1.1",
|
|
51
52
|
"chalk": "^5.3.0",
|
|
52
53
|
"chalk-template": "^1.1.0",
|
|
53
54
|
"clean-stack": "^5.2.0",
|
|
55
|
+
"cli-cursor": "^4.0.0",
|
|
56
|
+
"cli-spinners": "^2.9.2",
|
|
54
57
|
"cli-table3": "^0.6.3",
|
|
55
58
|
"dayjs": "^1.11.10",
|
|
56
59
|
"execa": "^8.0.1",
|
|
57
60
|
"fast-levenshtein": "^3.0.0",
|
|
58
|
-
"
|
|
61
|
+
"figures": "^6.1.0",
|
|
62
|
+
"find-up": "^7.0.0",
|
|
59
63
|
"fs-extra": "^11.2.0",
|
|
60
64
|
"get-port": "^7.0.0",
|
|
61
65
|
"got": "^13.0.0",
|
|
62
66
|
"graphql": "^16.8.1",
|
|
63
|
-
"graphql-ws": "^5.
|
|
64
|
-
"ignore": "^5.3.
|
|
67
|
+
"graphql-ws": "^5.15.0",
|
|
68
|
+
"ignore": "^5.3.1",
|
|
69
|
+
"indent-string": "^5.0.0",
|
|
70
|
+
"is-interactive": "^2.0.0",
|
|
71
|
+
"is-unicode-supported": "^2.0.0",
|
|
65
72
|
"is-wsl": "^3.1.0",
|
|
66
|
-
"mimic-
|
|
73
|
+
"mimic-function": "^5.0.0",
|
|
67
74
|
"ms": "^2.1.3",
|
|
68
75
|
"node-notifier": "^10.0.1",
|
|
69
76
|
"normalize-package-data": "^6.0.0",
|
|
70
77
|
"normalize-path": "^3.0.0",
|
|
71
|
-
"open": "^
|
|
72
|
-
"
|
|
73
|
-
"p-
|
|
74
|
-
"p-queue": "^7.4.1",
|
|
78
|
+
"open": "^10.1.0",
|
|
79
|
+
"p-map": "^7.0.1",
|
|
80
|
+
"p-queue": "^8.0.1",
|
|
75
81
|
"p-retry": "^6.2.0",
|
|
76
82
|
"p-timeout": "^6.1.2",
|
|
77
83
|
"pluralize": "^8.0.0",
|
|
78
|
-
"
|
|
79
|
-
"semver": "^7.5.4",
|
|
84
|
+
"semver": "^7.6.0",
|
|
80
85
|
"serialize-error": "^11.0.3",
|
|
86
|
+
"simple-git": "^3.22.0",
|
|
87
|
+
"stdin-discarder": "^0.2.2",
|
|
88
|
+
"string-width": "^7.1.0",
|
|
81
89
|
"strip-ansi": "^7.1.0",
|
|
90
|
+
"terminal-link": "^3.0.0",
|
|
82
91
|
"ts-dedent": "^2.2.0",
|
|
83
92
|
"watcher": "^2.3.0",
|
|
84
93
|
"which": "^4.0.0",
|
|
@@ -87,49 +96,47 @@
|
|
|
87
96
|
},
|
|
88
97
|
"devDependencies": {
|
|
89
98
|
"@changesets/cli": "^2.27.1",
|
|
90
|
-
"@graphql-codegen/add": "^5.0.
|
|
91
|
-
"@graphql-codegen/cli": "^5.0.
|
|
92
|
-
"@graphql-codegen/typescript": "^4.0.
|
|
93
|
-
"@graphql-codegen/typescript-operations": "^4.0
|
|
94
|
-
"@swc-node/register": "^1.
|
|
95
|
-
"@swc/cli": "^0.
|
|
96
|
-
"@swc/core": "^1.
|
|
97
|
-
"@types/eslint": "^8.56.
|
|
99
|
+
"@graphql-codegen/add": "^5.0.2",
|
|
100
|
+
"@graphql-codegen/cli": "^5.0.2",
|
|
101
|
+
"@graphql-codegen/typescript": "^4.0.6",
|
|
102
|
+
"@graphql-codegen/typescript-operations": "^4.2.0",
|
|
103
|
+
"@swc-node/register": "^1.9.0",
|
|
104
|
+
"@swc/cli": "^0.3.10",
|
|
105
|
+
"@swc/core": "^1.4.6",
|
|
106
|
+
"@types/eslint": "^8.56.5",
|
|
98
107
|
"@types/fast-levenshtein": "^0.0.4",
|
|
99
108
|
"@types/fs-extra": "^11.0.4",
|
|
100
109
|
"@types/ms": "^0.7.34",
|
|
101
|
-
"@types/node": "^18.19.
|
|
110
|
+
"@types/node": "^18.19.22",
|
|
102
111
|
"@types/node-notifier": "^8.0.5",
|
|
103
112
|
"@types/normalize-path": "^3.0.2",
|
|
104
113
|
"@types/pluralize": "^0.0.33",
|
|
105
|
-
"@types/prompts": "^2.4.9",
|
|
106
114
|
"@types/which": "^3.0.3",
|
|
107
115
|
"@types/ws": "^8.5.10",
|
|
108
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
109
|
-
"@typescript-eslint/parser": "^
|
|
116
|
+
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
|
117
|
+
"@typescript-eslint/parser": "^7.1.1",
|
|
110
118
|
"concurrently": "^8.2.2",
|
|
111
|
-
"cspell": "^
|
|
112
|
-
"eslint": "^8.
|
|
119
|
+
"cspell": "^8.6.0",
|
|
120
|
+
"eslint": "^8.57.0",
|
|
113
121
|
"eslint-config-prettier": "^9.1.0",
|
|
114
122
|
"eslint-import-resolver-typescript": "^3.6.1",
|
|
115
123
|
"eslint-plugin-import": "^2.29.1",
|
|
116
|
-
"eslint-plugin-jsdoc": "^
|
|
117
|
-
"eslint-plugin-lodash": "^7.4.0",
|
|
124
|
+
"eslint-plugin-jsdoc": "^48.2.1",
|
|
118
125
|
"eslint-plugin-only-warn": "^1.1.0",
|
|
119
|
-
"eslint-plugin-unicorn": "^
|
|
120
|
-
"nock": "^13.5.
|
|
121
|
-
"prettier": "^3.
|
|
126
|
+
"eslint-plugin-unicorn": "^51.0.1",
|
|
127
|
+
"nock": "^13.5.4",
|
|
128
|
+
"prettier": "^3.2.5",
|
|
122
129
|
"prettier-plugin-organize-imports": "^3.2.4",
|
|
123
|
-
"prettier-plugin-packagejson": "^2.4.
|
|
130
|
+
"prettier-plugin-packagejson": "^2.4.12",
|
|
124
131
|
"remark": "^15.0.1",
|
|
125
132
|
"remark-gfm": "^4.0.0",
|
|
126
133
|
"remark-toc": "^9.0.0",
|
|
127
134
|
"rimraf": "^5.0.5",
|
|
128
|
-
"type-fest": "^4.
|
|
129
|
-
"typescript": "^5.
|
|
130
|
-
"vitest": "^
|
|
135
|
+
"type-fest": "^4.12.0",
|
|
136
|
+
"typescript": "^5.4.2",
|
|
137
|
+
"vitest": "^1.3.1"
|
|
131
138
|
},
|
|
132
139
|
"engines": {
|
|
133
|
-
"node": ">=
|
|
140
|
+
"node": ">=18.0.0"
|
|
134
141
|
}
|
|
135
142
|
}
|
package/bin/dev.cmd
DELETED
package/bin/dev.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node --loader @swc-node/register/esm --no-warnings
|
|
2
|
-
|
|
3
|
-
import process from "node:process";
|
|
4
|
-
import { ggt } from "../src/ggt.js";
|
|
5
|
-
import { workspacePath } from "../src/services/util/paths.js";
|
|
6
|
-
|
|
7
|
-
process.env["NODE_ENV"] ??= "development";
|
|
8
|
-
process.env["GGT_ENV"] ??= "development";
|
|
9
|
-
process.env["GGT_SENTRY_ENABLED"] ??= "false";
|
|
10
|
-
process.env["GGT_CONFIG_DIR"] ??= workspacePath("tmp/config");
|
|
11
|
-
process.env["GGT_CACHE_DIR"] ??= workspacePath("tmp/cache");
|
|
12
|
-
process.env["GGT_DATA_DIR"] ??= workspacePath("tmp/data");
|
|
13
|
-
|
|
14
|
-
await ggt();
|
package/bin/run.cmd
DELETED
package/bin/run.js
DELETED
package/lib/commands/sync.js
DELETED
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
import dayjs from "dayjs";
|
|
2
|
-
import ms from "ms";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import Watcher from "watcher";
|
|
5
|
-
import which from "which";
|
|
6
|
-
import { config } from "../services/config/config.js";
|
|
7
|
-
import { Changes } from "../services/filesync/changes.js";
|
|
8
|
-
import { YarnNotFoundError } from "../services/filesync/error.js";
|
|
9
|
-
import { FileSync, FileSyncArgs } from "../services/filesync/filesync.js";
|
|
10
|
-
import { notify } from "../services/output/notify.js";
|
|
11
|
-
import { reportErrorAndExit } from "../services/output/report.js";
|
|
12
|
-
import { sprint } from "../services/output/sprint.js";
|
|
13
|
-
import { debounce } from "../services/util/function.js";
|
|
14
|
-
import { isAbortError } from "../services/util/is.js";
|
|
15
|
-
export const usage = ()=>sprint`
|
|
16
|
-
Sync your Gadget environment's source code with your local filesystem.
|
|
17
|
-
|
|
18
|
-
{bold USAGE}
|
|
19
|
-
ggt sync [DIRECTORY]
|
|
20
|
-
|
|
21
|
-
{bold ARGUMENTS}
|
|
22
|
-
DIRECTORY The directory to sync files to (default: ".")
|
|
23
|
-
|
|
24
|
-
{bold FLAGS}
|
|
25
|
-
-a, --app=<name> The Gadget application to sync files to
|
|
26
|
-
--prefer=<filesystem> Prefer "local" or "gadget" conflicting changes
|
|
27
|
-
--once Sync once and exit
|
|
28
|
-
--force Sync regardless of local filesystem state
|
|
29
|
-
|
|
30
|
-
{bold DESCRIPTION}
|
|
31
|
-
Sync allows you to synchronize your Gadget application's source
|
|
32
|
-
code with your local filesystem.
|
|
33
|
-
|
|
34
|
-
While ggt sync is running, local file changes are immediately
|
|
35
|
-
reflected within Gadget, while files that are changed in Gadget are
|
|
36
|
-
immediately saved to your local filesystem.
|
|
37
|
-
|
|
38
|
-
Ideal for:
|
|
39
|
-
• Local development with editors like VSCode
|
|
40
|
-
• Storing source code in a Git repository like GitHub
|
|
41
|
-
|
|
42
|
-
Sync looks for a ".ignore" file to exclude certain files/directories
|
|
43
|
-
from being synced. The format is identical to Git's.
|
|
44
|
-
|
|
45
|
-
These files are always ignored:
|
|
46
|
-
• .DS_Store
|
|
47
|
-
• .gadget
|
|
48
|
-
• .git
|
|
49
|
-
• node_modules
|
|
50
|
-
|
|
51
|
-
Note:
|
|
52
|
-
• Sync only works with your development environment
|
|
53
|
-
• Avoid deleting/moving all your files while sync is running
|
|
54
|
-
• Gadget only supports Yarn v1 for dependency installation
|
|
55
|
-
|
|
56
|
-
{bold EXAMPLE}
|
|
57
|
-
$ ggt sync ~/gadget/example --app example
|
|
58
|
-
|
|
59
|
-
App example
|
|
60
|
-
Editor https://example.gadget.app/edit
|
|
61
|
-
Playground https://example.gadget.app/api/graphql/playground
|
|
62
|
-
Docs https://docs.gadget.dev/api/example
|
|
63
|
-
|
|
64
|
-
Endpoints
|
|
65
|
-
• https://example.gadget.app
|
|
66
|
-
• https://example--development.gadget.app
|
|
67
|
-
|
|
68
|
-
Watching for file changes... {gray Press Ctrl+C to stop}
|
|
69
|
-
|
|
70
|
-
→ Sent {gray 09:06:25 AM}
|
|
71
|
-
{greenBright routes/GET-hello.js + created}
|
|
72
|
-
|
|
73
|
-
→ Sent {gray 09:06:49 AM}
|
|
74
|
-
{blueBright routes/GET-hello.js ± updated}
|
|
75
|
-
|
|
76
|
-
← Received {gray 09:06:54 AM}
|
|
77
|
-
{blueBright routes/GET-hello.js ± updated}
|
|
78
|
-
|
|
79
|
-
← Received {gray 09:06:56 AM}
|
|
80
|
-
{redBright routes/GET-hello.js - deleted}
|
|
81
|
-
^C Stopping... {gray press Ctrl+C again to force}
|
|
82
|
-
|
|
83
|
-
Goodbye!
|
|
84
|
-
`;
|
|
85
|
-
export const args = {
|
|
86
|
-
...FileSyncArgs,
|
|
87
|
-
"--once": Boolean,
|
|
88
|
-
"--file-push-delay": {
|
|
89
|
-
type: Number,
|
|
90
|
-
default: ms("100ms")
|
|
91
|
-
},
|
|
92
|
-
"--file-watch-debounce": {
|
|
93
|
-
type: Number,
|
|
94
|
-
default: ms("300ms")
|
|
95
|
-
},
|
|
96
|
-
"--file-watch-poll-interval": {
|
|
97
|
-
type: Number,
|
|
98
|
-
default: ms("3s")
|
|
99
|
-
},
|
|
100
|
-
"--file-watch-poll-timeout": {
|
|
101
|
-
type: Number,
|
|
102
|
-
default: ms("20s")
|
|
103
|
-
},
|
|
104
|
-
"--file-watch-rename-timeout": {
|
|
105
|
-
type: Number,
|
|
106
|
-
default: ms("1.25s")
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
/**
|
|
110
|
-
* Runs the sync process until it is stopped or an error occurs.
|
|
111
|
-
*/ export const command = async (ctx)=>{
|
|
112
|
-
if (!which.sync("yarn", {
|
|
113
|
-
nothrow: true
|
|
114
|
-
})) {
|
|
115
|
-
throw new YarnNotFoundError();
|
|
116
|
-
}
|
|
117
|
-
const filesync = await FileSync.init(ctx);
|
|
118
|
-
await filesync.sync();
|
|
119
|
-
if (ctx.args["--once"]) {
|
|
120
|
-
ctx.log.println("Done!");
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* A list of filepaths that have changed because we (this ggt process)
|
|
125
|
-
* modified them. This is used to avoid reacting to filesystem events
|
|
126
|
-
* that we caused, which would cause an infinite loop.
|
|
127
|
-
*/ const recentWritesToLocalFilesystem = new Map();
|
|
128
|
-
const clearRecentWritesInterval = setInterval(()=>{
|
|
129
|
-
for (const [path, timestamp] of recentWritesToLocalFilesystem){
|
|
130
|
-
if (dayjs().isAfter(timestamp + ms("5s"))) {
|
|
131
|
-
// this change should have been seen by now
|
|
132
|
-
recentWritesToLocalFilesystem.delete(path);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}, ms("1s")).unref();
|
|
136
|
-
/**
|
|
137
|
-
* Subscribe to file changes on Gadget and apply them to the local
|
|
138
|
-
* filesystem.
|
|
139
|
-
*/ const unsubscribeFromGadgetChanges = filesync.subscribeToGadgetChanges({
|
|
140
|
-
onError: (error)=>ctx.abort(error),
|
|
141
|
-
beforeChanges: ({ changed, deleted })=>{
|
|
142
|
-
// add all the files and directories we're about to touch to
|
|
143
|
-
// recentWritesToLocalFilesystem so that we don't send them back
|
|
144
|
-
// to Gadget
|
|
145
|
-
for (const filepath of [
|
|
146
|
-
...changed,
|
|
147
|
-
...deleted
|
|
148
|
-
]){
|
|
149
|
-
recentWritesToLocalFilesystem.set(filepath, Date.now());
|
|
150
|
-
let dir = path.dirname(filepath);
|
|
151
|
-
while(dir !== "."){
|
|
152
|
-
recentWritesToLocalFilesystem.set(dir + "/", Date.now());
|
|
153
|
-
dir = path.dirname(dir);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
/**
|
|
159
|
-
* A buffer of local file changes to send to Gadget.
|
|
160
|
-
*/ const localChangesBuffer = new Changes();
|
|
161
|
-
/**
|
|
162
|
-
* A debounced function that sends the local file changes to Gadget.
|
|
163
|
-
*/ const sendChangesToGadget = debounce(ctx.args["--file-push-delay"], ()=>{
|
|
164
|
-
const changes = new Changes(localChangesBuffer.entries());
|
|
165
|
-
localChangesBuffer.clear();
|
|
166
|
-
filesync.sendChangesToGadget({
|
|
167
|
-
changes
|
|
168
|
-
}).catch((error)=>ctx.abort(error));
|
|
169
|
-
});
|
|
170
|
-
ctx.log.debug("watching", {
|
|
171
|
-
path: filesync.directory.path
|
|
172
|
-
});
|
|
173
|
-
/**
|
|
174
|
-
* Watches the local filesystem for changes.
|
|
175
|
-
*/ const fileWatcher = new Watcher(filesync.directory.path, {
|
|
176
|
-
// don't emit an event for every watched file on boot
|
|
177
|
-
ignoreInitial: true,
|
|
178
|
-
// don't emit changes to .gadget/ files because they're readonly (Gadget manages them)
|
|
179
|
-
ignore: (path)=>filesync.directory.relative(path).startsWith(".gadget") || filesync.directory.ignores(path),
|
|
180
|
-
renameDetection: true,
|
|
181
|
-
recursive: true,
|
|
182
|
-
debounce: ctx.args["--file-watch-debounce"],
|
|
183
|
-
pollingInterval: ctx.args["--file-watch-poll-interval"],
|
|
184
|
-
pollingTimeout: ctx.args["--file-watch-poll-timeout"],
|
|
185
|
-
renameTimeout: ctx.args["--file-watch-rename-timeout"]
|
|
186
|
-
}, (event, absolutePath, renamedPath)=>{
|
|
187
|
-
const filepath = event === "rename" || event === "renameDir" ? renamedPath : absolutePath;
|
|
188
|
-
const isDirectory = event === "renameDir" || event === "addDir" || event === "unlinkDir";
|
|
189
|
-
const normalizedPath = filesync.directory.normalize(filepath, isDirectory);
|
|
190
|
-
ctx.log.trace("file event", {
|
|
191
|
-
event,
|
|
192
|
-
isDirectory,
|
|
193
|
-
path: normalizedPath
|
|
194
|
-
});
|
|
195
|
-
if (filepath === filesync.directory.absolute(".ignore")) {
|
|
196
|
-
filesync.directory.loadIgnoreFile().catch((error)=>ctx.abort(error));
|
|
197
|
-
} else if (filesync.directory.ignores(filepath)) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (recentWritesToLocalFilesystem.delete(normalizedPath)) {
|
|
201
|
-
ctx.log.trace("ignoring event because we caused it", {
|
|
202
|
-
event,
|
|
203
|
-
path: normalizedPath
|
|
204
|
-
});
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
switch(event){
|
|
208
|
-
case "add":
|
|
209
|
-
case "addDir":
|
|
210
|
-
localChangesBuffer.set(normalizedPath, {
|
|
211
|
-
type: "create"
|
|
212
|
-
});
|
|
213
|
-
break;
|
|
214
|
-
case "rename":
|
|
215
|
-
case "renameDir":
|
|
216
|
-
{
|
|
217
|
-
const oldNormalizedPath = filesync.directory.normalize(absolutePath, isDirectory);
|
|
218
|
-
localChangesBuffer.set(normalizedPath, {
|
|
219
|
-
type: "create",
|
|
220
|
-
oldPath: oldNormalizedPath
|
|
221
|
-
});
|
|
222
|
-
break;
|
|
223
|
-
}
|
|
224
|
-
case "change":
|
|
225
|
-
{
|
|
226
|
-
localChangesBuffer.set(normalizedPath, {
|
|
227
|
-
type: "update"
|
|
228
|
-
});
|
|
229
|
-
break;
|
|
230
|
-
}
|
|
231
|
-
case "unlink":
|
|
232
|
-
case "unlinkDir":
|
|
233
|
-
{
|
|
234
|
-
localChangesBuffer.set(normalizedPath, {
|
|
235
|
-
type: "delete"
|
|
236
|
-
});
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
sendChangesToGadget();
|
|
241
|
-
}).once("error", (error)=>ctx.abort(error));
|
|
242
|
-
ctx.log.printlns`
|
|
243
|
-
ggt v${config.version}
|
|
244
|
-
|
|
245
|
-
App ${filesync.app.slug}
|
|
246
|
-
Editor https://${filesync.app.primaryDomain}/edit
|
|
247
|
-
Playground https://${filesync.app.primaryDomain}/api/graphql/playground
|
|
248
|
-
Docs https://docs.gadget.dev/api/${filesync.app.slug}
|
|
249
|
-
|
|
250
|
-
Endpoints ${filesync.app.hasSplitEnvironments ? `
|
|
251
|
-
• https://${filesync.app.primaryDomain}
|
|
252
|
-
• https://${filesync.app.slug}--development.gadget.app` : `
|
|
253
|
-
• https://${filesync.app.primaryDomain}`}
|
|
254
|
-
|
|
255
|
-
Watching for file changes... {gray Press Ctrl+C to stop}
|
|
256
|
-
`;
|
|
257
|
-
ctx.onAbort(async (reason)=>{
|
|
258
|
-
ctx.log.info("stopping", {
|
|
259
|
-
reason
|
|
260
|
-
});
|
|
261
|
-
unsubscribeFromGadgetChanges();
|
|
262
|
-
fileWatcher.close();
|
|
263
|
-
clearInterval(clearRecentWritesInterval);
|
|
264
|
-
sendChangesToGadget.flush();
|
|
265
|
-
try {
|
|
266
|
-
await filesync.idle();
|
|
267
|
-
} catch (error) {
|
|
268
|
-
ctx.log.error("error while waiting for idle", {
|
|
269
|
-
error
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
if (isAbortError(reason)) {
|
|
273
|
-
ctx.log.printlns("Goodbye!");
|
|
274
|
-
return;
|
|
275
|
-
}
|
|
276
|
-
notify(ctx, {
|
|
277
|
-
subtitle: "Uh oh!",
|
|
278
|
-
message: "An error occurred while syncing files"
|
|
279
|
-
});
|
|
280
|
-
await reportErrorAndExit(ctx, reason);
|
|
281
|
-
});
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
//# sourceMappingURL=sync.js.map
|
package/lib/commands/sync.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/commands/sync.ts"],"sourcesContent":["import dayjs from \"dayjs\";\nimport ms from \"ms\";\nimport path from \"node:path\";\nimport Watcher from \"watcher\";\nimport which from \"which\";\nimport type { ArgsDefinition } from \"../services/command/arg.js\";\nimport type { Command, Usage } from \"../services/command/command.js\";\nimport { config } from \"../services/config/config.js\";\nimport { Changes } from \"../services/filesync/changes.js\";\nimport { YarnNotFoundError } from \"../services/filesync/error.js\";\nimport { FileSync, FileSyncArgs } from \"../services/filesync/filesync.js\";\nimport { notify } from \"../services/output/notify.js\";\nimport { reportErrorAndExit } from \"../services/output/report.js\";\nimport { sprint } from \"../services/output/sprint.js\";\nimport { debounce } from \"../services/util/function.js\";\nimport { isAbortError } from \"../services/util/is.js\";\n\nexport const usage: Usage = () => sprint`\n Sync your Gadget environment's source code with your local filesystem.\n\n {bold USAGE}\n ggt sync [DIRECTORY]\n\n {bold ARGUMENTS}\n DIRECTORY The directory to sync files to (default: \".\")\n\n {bold FLAGS}\n -a, --app=<name> The Gadget application to sync files to\n --prefer=<filesystem> Prefer \"local\" or \"gadget\" conflicting changes\n --once Sync once and exit\n --force Sync regardless of local filesystem state\n\n {bold DESCRIPTION}\n Sync allows you to synchronize your Gadget application's source\n code with your local filesystem.\n\n While ggt sync is running, local file changes are immediately\n reflected within Gadget, while files that are changed in Gadget are\n immediately saved to your local filesystem.\n\n Ideal for:\n • Local development with editors like VSCode\n • Storing source code in a Git repository like GitHub\n\n Sync looks for a \".ignore\" file to exclude certain files/directories\n from being synced. The format is identical to Git's.\n\n These files are always ignored:\n • .DS_Store\n • .gadget\n • .git\n • node_modules\n\n Note:\n • Sync only works with your development environment\n • Avoid deleting/moving all your files while sync is running\n • Gadget only supports Yarn v1 for dependency installation\n\n {bold EXAMPLE}\n $ ggt sync ~/gadget/example --app example\n\n App example\n Editor https://example.gadget.app/edit\n Playground https://example.gadget.app/api/graphql/playground\n Docs https://docs.gadget.dev/api/example\n\n Endpoints\n • https://example.gadget.app\n • https://example--development.gadget.app\n\n Watching for file changes... {gray Press Ctrl+C to stop}\n\n → Sent {gray 09:06:25 AM}\n {greenBright routes/GET-hello.js + created}\n\n → Sent {gray 09:06:49 AM}\n {blueBright routes/GET-hello.js ± updated}\n\n ← Received {gray 09:06:54 AM}\n {blueBright routes/GET-hello.js ± updated}\n\n ← Received {gray 09:06:56 AM}\n {redBright routes/GET-hello.js - deleted}\n ^C Stopping... {gray press Ctrl+C again to force}\n\n Goodbye!\n`;\n\nexport const args = {\n ...FileSyncArgs,\n \"--once\": Boolean,\n \"--file-push-delay\": { type: Number, default: ms(\"100ms\") },\n \"--file-watch-debounce\": { type: Number, default: ms(\"300ms\") },\n \"--file-watch-poll-interval\": { type: Number, default: ms(\"3s\") },\n \"--file-watch-poll-timeout\": { type: Number, default: ms(\"20s\") },\n \"--file-watch-rename-timeout\": { type: Number, default: ms(\"1.25s\") },\n} satisfies ArgsDefinition;\n\nexport type SyncArgs = typeof args;\n\n/**\n * Runs the sync process until it is stopped or an error occurs.\n */\nexport const command: Command<SyncArgs> = async (ctx) => {\n if (!which.sync(\"yarn\", { nothrow: true })) {\n throw new YarnNotFoundError();\n }\n\n const filesync = await FileSync.init(ctx);\n await filesync.sync();\n\n if (ctx.args[\"--once\"]) {\n ctx.log.println(\"Done!\");\n return;\n }\n\n /**\n * A list of filepaths that have changed because we (this ggt process)\n * modified them. This is used to avoid reacting to filesystem events\n * that we caused, which would cause an infinite loop.\n */\n const recentWritesToLocalFilesystem = new Map<string, number>();\n\n const clearRecentWritesInterval = setInterval(() => {\n for (const [path, timestamp] of recentWritesToLocalFilesystem) {\n if (dayjs().isAfter(timestamp + ms(\"5s\"))) {\n // this change should have been seen by now\n recentWritesToLocalFilesystem.delete(path);\n }\n }\n }, ms(\"1s\")).unref();\n\n /**\n * Subscribe to file changes on Gadget and apply them to the local\n * filesystem.\n */\n const unsubscribeFromGadgetChanges = filesync.subscribeToGadgetChanges({\n onError: (error) => ctx.abort(error),\n beforeChanges: ({ changed, deleted }) => {\n // add all the files and directories we're about to touch to\n // recentWritesToLocalFilesystem so that we don't send them back\n // to Gadget\n for (const filepath of [...changed, ...deleted]) {\n recentWritesToLocalFilesystem.set(filepath, Date.now());\n\n let dir = path.dirname(filepath);\n while (dir !== \".\") {\n recentWritesToLocalFilesystem.set(dir + \"/\", Date.now());\n dir = path.dirname(dir);\n }\n }\n },\n });\n\n /**\n * A buffer of local file changes to send to Gadget.\n */\n const localChangesBuffer = new Changes();\n\n /**\n * A debounced function that sends the local file changes to Gadget.\n */\n const sendChangesToGadget = debounce(ctx.args[\"--file-push-delay\"], () => {\n const changes = new Changes(localChangesBuffer.entries());\n localChangesBuffer.clear();\n filesync.sendChangesToGadget({ changes }).catch((error) => ctx.abort(error));\n });\n\n ctx.log.debug(\"watching\", { path: filesync.directory.path });\n\n /**\n * Watches the local filesystem for changes.\n */\n const fileWatcher = new Watcher(\n filesync.directory.path,\n {\n // don't emit an event for every watched file on boot\n ignoreInitial: true,\n // don't emit changes to .gadget/ files because they're readonly (Gadget manages them)\n ignore: (path: string) => filesync.directory.relative(path).startsWith(\".gadget\") || filesync.directory.ignores(path),\n renameDetection: true,\n recursive: true,\n debounce: ctx.args[\"--file-watch-debounce\"],\n pollingInterval: ctx.args[\"--file-watch-poll-interval\"],\n pollingTimeout: ctx.args[\"--file-watch-poll-timeout\"],\n renameTimeout: ctx.args[\"--file-watch-rename-timeout\"],\n },\n (event: string, absolutePath: string, renamedPath: string) => {\n const filepath = event === \"rename\" || event === \"renameDir\" ? renamedPath : absolutePath;\n const isDirectory = event === \"renameDir\" || event === \"addDir\" || event === \"unlinkDir\";\n const normalizedPath = filesync.directory.normalize(filepath, isDirectory);\n\n ctx.log.trace(\"file event\", { event, isDirectory, path: normalizedPath });\n\n if (filepath === filesync.directory.absolute(\".ignore\")) {\n filesync.directory.loadIgnoreFile().catch((error) => ctx.abort(error));\n } else if (filesync.directory.ignores(filepath)) {\n return;\n }\n\n if (recentWritesToLocalFilesystem.delete(normalizedPath)) {\n ctx.log.trace(\"ignoring event because we caused it\", { event, path: normalizedPath });\n return;\n }\n\n switch (event) {\n case \"add\":\n case \"addDir\":\n localChangesBuffer.set(normalizedPath, { type: \"create\" });\n break;\n case \"rename\":\n case \"renameDir\": {\n const oldNormalizedPath = filesync.directory.normalize(absolutePath, isDirectory);\n localChangesBuffer.set(normalizedPath, { type: \"create\", oldPath: oldNormalizedPath });\n break;\n }\n case \"change\": {\n localChangesBuffer.set(normalizedPath, { type: \"update\" });\n break;\n }\n case \"unlink\":\n case \"unlinkDir\": {\n localChangesBuffer.set(normalizedPath, { type: \"delete\" });\n break;\n }\n }\n\n sendChangesToGadget();\n },\n ).once(\"error\", (error) => ctx.abort(error));\n\n ctx.log.printlns`\n ggt v${config.version}\n\n App ${filesync.app.slug}\n Editor https://${filesync.app.primaryDomain}/edit\n Playground https://${filesync.app.primaryDomain}/api/graphql/playground\n Docs https://docs.gadget.dev/api/${filesync.app.slug}\n\n Endpoints ${\n filesync.app.hasSplitEnvironments\n ? `\n • https://${filesync.app.primaryDomain}\n • https://${filesync.app.slug}--development.gadget.app`\n : `\n • https://${filesync.app.primaryDomain}`\n }\n\n Watching for file changes... {gray Press Ctrl+C to stop}\n `;\n\n ctx.onAbort(async (reason) => {\n ctx.log.info(\"stopping\", { reason });\n\n unsubscribeFromGadgetChanges();\n fileWatcher.close();\n clearInterval(clearRecentWritesInterval);\n sendChangesToGadget.flush();\n\n try {\n await filesync.idle();\n } catch (error) {\n ctx.log.error(\"error while waiting for idle\", { error });\n }\n\n if (isAbortError(reason)) {\n ctx.log.printlns(\"Goodbye!\");\n return;\n }\n\n notify(ctx, { subtitle: \"Uh oh!\", message: \"An error occurred while syncing files\" });\n await reportErrorAndExit(ctx, reason);\n });\n};\n"],"names":["dayjs","ms","path","Watcher","which","config","Changes","YarnNotFoundError","FileSync","FileSyncArgs","notify","reportErrorAndExit","sprint","debounce","isAbortError","usage","args","Boolean","type","Number","default","command","ctx","sync","nothrow","filesync","init","log","println","recentWritesToLocalFilesystem","Map","clearRecentWritesInterval","setInterval","timestamp","isAfter","delete","unref","unsubscribeFromGadgetChanges","subscribeToGadgetChanges","onError","error","abort","beforeChanges","changed","deleted","filepath","set","Date","now","dir","dirname","localChangesBuffer","sendChangesToGadget","changes","entries","clear","catch","debug","directory","fileWatcher","ignoreInitial","ignore","relative","startsWith","ignores","renameDetection","recursive","pollingInterval","pollingTimeout","renameTimeout","event","absolutePath","renamedPath","isDirectory","normalizedPath","normalize","trace","absolute","loadIgnoreFile","oldNormalizedPath","oldPath","once","printlns","version","app","slug","primaryDomain","hasSplitEnvironments","onAbort","reason","info","close","clearInterval","flush","idle","subtitle","message"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/services/app/edit/client.ts"],"sourcesContent":["import type { ExecutionResult } from \"graphql-ws\";\nimport { createClient } from \"graphql-ws\";\nimport assert from \"node:assert\";\nimport type { ClientRequestArgs } from \"node:http\";\nimport PQueue from \"p-queue\";\nimport type { Promisable } from \"type-fest\";\nimport WebSocket from \"ws\";\nimport type { Context } from \"../../command/context.js\";\nimport { config } from \"../../config/config.js\";\nimport { loadCookie } from \"../../http/auth.js\";\nimport { http, type HttpOptions } from \"../../http/http.js\";\nimport { noop, unthunk, type Thunk } from \"../../util/function.js\";\nimport { isObject } from \"../../util/is.js\";\nimport { EditError } from \"./error.js\";\nimport type { GraphQLMutation, GraphQLQuery, GraphQLSubscription } from \"./operation.js\";\n\nenum ConnectionStatus {\n CONNECTED,\n DISCONNECTED,\n RECONNECTING,\n}\n\n/**\n * Client is a GraphQL client connected to a Gadget application's\n * /edit/api/graphql endpoint.\n */\nexport class Client {\n // assume the client is going to connect\n status = ConnectionStatus.CONNECTED;\n\n readonly ctx: Context;\n\n private _graphqlWsClient: ReturnType<typeof createClient>;\n\n constructor(ctx: Context) {\n this.ctx = ctx.child({ name: \"client\" });\n assert(ctx.app, \"missing app when creating edit client\");\n\n let subdomain = ctx.app.slug;\n if (ctx.app.hasSplitEnvironments) {\n subdomain += \"--development\";\n }\n\n this._graphqlWsClient = createClient({\n url: `wss://${subdomain}.${config.domains.app}/edit/api/graphql-ws`,\n shouldRetry: () => true,\n webSocketImpl: class extends WebSocket {\n constructor(address: string | URL, protocols?: string | string[], wsOptions?: WebSocket.ClientOptions | ClientRequestArgs) {\n // this cookie should be available since we were given an app which requires a cookie to load\n const cookie = loadCookie();\n assert(cookie, \"missing cookie when connecting to GraphQL API\");\n\n super(address, protocols, {\n signal: ctx.signal,\n ...wsOptions,\n headers: {\n ...wsOptions?.headers,\n \"user-agent\": config.versionFull,\n cookie,\n },\n });\n }\n },\n on: {\n connecting: () => {\n switch (this.status) {\n case ConnectionStatus.DISCONNECTED:\n this.status = ConnectionStatus.RECONNECTING;\n this.ctx.log.info(\"reconnecting\");\n break;\n case ConnectionStatus.RECONNECTING:\n this.ctx.log.info(\"retrying\");\n break;\n default:\n this.ctx.log.debug(\"connecting\");\n break;\n }\n },\n connected: () => {\n if (this.status === ConnectionStatus.RECONNECTING) {\n this.ctx.log.info(\"reconnected\");\n } else {\n this.ctx.log.debug(\"connected\");\n }\n\n // let the other on connected listeners see what status we're in\n setImmediate(() => (this.status = ConnectionStatus.CONNECTED));\n },\n closed: () => {\n this.status = ConnectionStatus.DISCONNECTED;\n this.ctx.log.debug(\"disconnected\");\n },\n error: (error) => {\n if (this.status === ConnectionStatus.RECONNECTING) {\n this.ctx.log.error(\"failed to reconnect\", { error });\n } else {\n this.ctx.log.error(\"connection error\", { error });\n }\n },\n },\n });\n }\n\n /**\n * Subscribe to a GraphQL subscription.\n */\n subscribe<Subscription extends GraphQLSubscription>(\n ctx: Context,\n {\n subscription,\n variables,\n onResponse,\n onError: optionsOnError,\n onComplete = noop,\n }: {\n subscription: Subscription;\n variables?: Thunk<Subscription[\"Variables\"]> | null;\n onResponse: (response: ExecutionResult<Subscription[\"Data\"], Subscription[\"Extensions\"]>) => Promisable<void>;\n onError: (error: EditError) => Promisable<void>;\n onComplete?: () => Promisable<void>;\n },\n ): () => void {\n let request = { query: subscription, variables: unthunk(variables) };\n\n const removeConnectedListener = this._graphqlWsClient.on(\"connected\", () => {\n if (this.status === ConnectionStatus.RECONNECTING) {\n request = { query: subscription, variables: unthunk(variables) };\n ctx.log.info(\"re-subscribing to graphql subscription\");\n }\n });\n\n const queue = new PQueue({ concurrency: 1 });\n const onError = (error: unknown): Promisable<void> => optionsOnError(new EditError(subscription, error));\n\n const unsubscribe = this._graphqlWsClient.subscribe<Subscription[\"Data\"], Subscription[\"Extensions\"]>(request, {\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n next: (response) => queue.add(() => onResponse(response)).catch(onError),\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n error: (error) => queue.add(() => onError(error)),\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n complete: () => queue.add(() => onComplete()).catch(onError),\n });\n\n return () => {\n removeConnectedListener();\n unsubscribe();\n };\n }\n\n /**\n * Execute a GraphQL query or mutation.\n */\n async execute<Operation extends GraphQLQuery | GraphQLMutation>(\n ctx: Context,\n request: {\n operation: Operation;\n variables?: Thunk<Operation[\"Variables\"]> | null;\n http?: HttpOptions;\n },\n ): Promise<ExecutionResult<Operation[\"Data\"], Operation[\"Extensions\"]>> {\n assert(ctx.app, \"missing app when executing GraphQL query\");\n\n const cookie = loadCookie();\n assert(cookie, \"missing cookie when executing GraphQL request\");\n\n let subdomain = ctx.app.slug;\n if (ctx.app.hasSplitEnvironments) {\n subdomain += \"--development\";\n }\n\n try {\n const json = await http({\n context: { ctx },\n method: \"POST\",\n url: `https://${subdomain}.${config.domains.app}/edit/api/graphql`,\n headers: { cookie },\n json: { query: request.operation, variables: unthunk(request.variables) },\n responseType: \"json\",\n resolveBodyOnly: true,\n throwHttpErrors: false,\n ...request.http,\n });\n\n if (!isObject(json) || (!(\"data\" in json) && !(\"errors\" in json))) {\n ctx.log.error(\"received invalid graphql response\", { error: json });\n throw json;\n }\n\n return json as Operation[\"Response\"];\n } catch (error) {\n throw new EditError(request.operation, error);\n }\n }\n\n /**\n * Close the connection to the server.\n */\n async dispose(): Promise<void> {\n await this._graphqlWsClient.dispose();\n }\n}\n"],"names":["createClient","assert","PQueue","WebSocket","config","loadCookie","http","noop","unthunk","isObject","EditError","ConnectionStatus","Client","subscribe","ctx","subscription","variables","onResponse","onError","optionsOnError","onComplete","request","query","removeConnectedListener","_graphqlWsClient","on","status","log","info","queue","concurrency","error","unsubscribe","next","response","add","catch","complete","execute","app","cookie","subdomain","slug","hasSplitEnvironments","json","context","method","url","domains","headers","operation","responseType","resolveBodyOnly","throwHttpErrors","dispose","constructor","child","name","shouldRetry","webSocketImpl","address","protocols","wsOptions","signal","versionFull","connecting","debug","connected","setImmediate","closed"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/services/app/edit/error.ts"],"sourcesContent":["import type { GraphQLError } from \"graphql\";\nimport assert from \"node:assert\";\nimport pluralize from \"pluralize\";\nimport type { CloseEvent, ErrorEvent } from \"ws\";\nimport { CLIError, IsBug } from \"../../output/report.js\";\nimport { sprint } from \"../../output/sprint.js\";\nimport { uniq } from \"../../util/collection.js\";\nimport { isCloseEvent, isError, isErrorEvent, isGraphQLErrors, isString } from \"../../util/is.js\";\nimport { serializeError } from \"../../util/object.js\";\nimport type { GraphQLMutation, GraphQLQuery, GraphQLSubscription } from \"./operation.js\";\n\nexport class EditError extends CLIError {\n isBug = IsBug.MAYBE;\n\n override cause: string | Error | readonly GraphQLError[] | CloseEvent | ErrorEvent;\n\n constructor(\n readonly request: GraphQLQuery | GraphQLMutation | GraphQLSubscription,\n cause: unknown,\n ) {\n super(\"An error occurred while communicating with Gadget\");\n\n // ErrorEvent and CloseEvent aren't serializable, so we reconstruct\n // them into an object. We discard the `target` property because\n // it's large and not that useful\n if (isErrorEvent(cause)) {\n this.cause = {\n type: cause.type,\n message: cause.message,\n error: serializeError(cause.error),\n } as ErrorEvent;\n } else if (isCloseEvent(cause)) {\n this.cause = {\n type: cause.type,\n code: cause.code,\n reason: cause.reason,\n wasClean: cause.wasClean,\n } as CloseEvent;\n } else {\n assert(\n isString(cause) || isError(cause) || isGraphQLErrors(cause),\n \"cause must be a string, Error, GraphQLError[], CloseEvent, or ErrorEvent\",\n );\n this.cause = cause;\n }\n }\n\n override render(): string {\n let body = \"\";\n\n switch (true) {\n case isGraphQLErrors(this.cause): {\n const errors = uniq(this.cause.map((x) => x.message));\n body = sprint`\n Gadget responded with the following ${pluralize(\"error\", errors.length, false)}:\n\n • ${errors.join(\"\\n • \")}\n `;\n break;\n }\n case isCloseEvent(this.cause):\n body = \"The connection to Gadget closed unexpectedly.\";\n break;\n case isErrorEvent(this.cause) || isError(this.cause):\n body = this.cause.message;\n break;\n default:\n body = this.cause;\n break;\n }\n\n return this.message + \"\\n\\n\" + body;\n }\n}\n"],"names":["assert","pluralize","CLIError","IsBug","sprint","uniq","isCloseEvent","isError","isErrorEvent","isGraphQLErrors","isString","serializeError","EditError","render","body","cause","errors","map","x","message","length","join","constructor","request","isBug","MAYBE","type","error","code","reason","wasClean"],"mappings":";AACA,OAAOA,YAAY,cAAc;AACjC,OAAOC,eAAe,YAAY;AAElC,SAASC,QAAQ,EAAEC,KAAK,QAAQ,yBAAyB;AACzD,SAASC,MAAM,QAAQ,yBAAyB;AAChD,SAASC,IAAI,QAAQ,2BAA2B;AAChD,SAASC,YAAY,EAAEC,OAAO,EAAEC,YAAY,EAAEC,eAAe,EAAEC,QAAQ,QAAQ,mBAAmB;AAClG,SAASC,cAAc,QAAQ,uBAAuB;AAGtD,OAAO,MAAMC,kBAAkBV;IAoCpBW,SAAiB;QACxB,IAAIC,OAAO;QAEX,OAAQ;YACN,KAAKL,gBAAgB,IAAI,CAACM,KAAK;gBAAG;oBAChC,MAAMC,SAASX,KAAK,IAAI,CAACU,KAAK,CAACE,GAAG,CAAC,CAACC,IAAMA,EAAEC,OAAO;oBACnDL,OAAOV,MAAM,CAAC;8CACwB,EAAEH,UAAU,SAASe,OAAOI,MAAM,EAAE,OAAO;;cAE3E,EAAEJ,OAAOK,IAAI,CAAC,oBAAoB;QACxC,CAAC;oBACD;gBACF;YACA,KAAKf,aAAa,IAAI,CAACS,KAAK;gBAC1BD,OAAO;gBACP;YACF,KAAKN,aAAa,IAAI,CAACO,KAAK,KAAKR,QAAQ,IAAI,CAACQ,KAAK;gBACjDD,OAAO,IAAI,CAACC,KAAK,CAACI,OAAO;gBACzB;YACF;gBACEL,OAAO,IAAI,CAACC,KAAK;gBACjB;QACJ;QAEA,OAAO,IAAI,CAACI,OAAO,GAAG,SAASL;IACjC;IAxDAQ,YACE,AAASC,OAA6D,EACtER,KAAc,CACd;QACA,KAAK,CAAC;;QARRS,uBAAAA,SAAAA,KAAAA;QAEA,uBAAST,SAAT,KAAA;aAGWQ,UAAAA;aALXC,QAAQrB,MAAMsB,KAAK;QAUjB,mEAAmE;QACnE,gEAAgE;QAChE,iCAAiC;QACjC,IAAIjB,aAAaO,QAAQ;YACvB,IAAI,CAACA,KAAK,GAAG;gBACXW,MAAMX,MAAMW,IAAI;gBAChBP,SAASJ,MAAMI,OAAO;gBACtBQ,OAAOhB,eAAeI,MAAMY,KAAK;YACnC;QACF,OAAO,IAAIrB,aAAaS,QAAQ;YAC9B,IAAI,CAACA,KAAK,GAAG;gBACXW,MAAMX,MAAMW,IAAI;gBAChBE,MAAMb,MAAMa,IAAI;gBAChBC,QAAQd,MAAMc,MAAM;gBACpBC,UAAUf,MAAMe,QAAQ;YAC1B;QACF,OAAO;YACL9B,OACEU,SAASK,UAAUR,QAAQQ,UAAUN,gBAAgBM,QACrD;YAEF,IAAI,CAACA,KAAK,GAAGA;QACf;IACF;AA4BF"}
|