@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.
Files changed (142) hide show
  1. package/README.md +165 -93
  2. package/lib/__generated__/graphql.js +66 -1
  3. package/lib/__generated__/graphql.js.map +1 -1
  4. package/lib/commands/deploy.js +328 -230
  5. package/lib/commands/deploy.js.map +1 -1
  6. package/lib/commands/dev.js +445 -0
  7. package/lib/commands/dev.js.map +1 -0
  8. package/lib/commands/list.js +27 -19
  9. package/lib/commands/list.js.map +1 -1
  10. package/lib/commands/login.js +15 -11
  11. package/lib/commands/login.js.map +1 -1
  12. package/lib/commands/logout.js +5 -5
  13. package/lib/commands/logout.js.map +1 -1
  14. package/lib/commands/open.js +200 -0
  15. package/lib/commands/open.js.map +1 -0
  16. package/lib/commands/pull.js +128 -0
  17. package/lib/commands/pull.js.map +1 -0
  18. package/lib/commands/push.js +126 -0
  19. package/lib/commands/push.js.map +1 -0
  20. package/lib/commands/root.js +46 -28
  21. package/lib/commands/root.js.map +1 -1
  22. package/lib/commands/status.js +61 -0
  23. package/lib/commands/status.js.map +1 -0
  24. package/lib/commands/version.js +6 -6
  25. package/lib/commands/version.js.map +1 -1
  26. package/lib/commands/whoami.js +6 -6
  27. package/lib/commands/whoami.js.map +1 -1
  28. package/lib/ggt.js +33 -8
  29. package/lib/ggt.js.map +1 -1
  30. package/lib/main.js +5 -0
  31. package/lib/main.js.map +1 -0
  32. package/lib/services/app/api/api.js +191 -0
  33. package/lib/services/app/api/api.js.map +1 -0
  34. package/lib/services/app/api/operation.js +12 -0
  35. package/lib/services/app/api/operation.js.map +1 -0
  36. package/lib/services/app/app.js +44 -10
  37. package/lib/services/app/app.js.map +1 -1
  38. package/lib/services/app/{edit/client.js → client.js} +29 -19
  39. package/lib/services/app/client.js.map +1 -0
  40. package/lib/services/app/edit/edit.js +67 -31
  41. package/lib/services/app/edit/edit.js.map +1 -1
  42. package/lib/services/app/edit/operation.js +4 -3
  43. package/lib/services/app/edit/operation.js.map +1 -1
  44. package/lib/services/app/{edit/error.js → error.js} +6 -6
  45. package/lib/services/app/error.js.map +1 -0
  46. package/lib/services/command/arg.js +4 -4
  47. package/lib/services/command/arg.js.map +1 -1
  48. package/lib/services/command/command.js +9 -7
  49. package/lib/services/command/command.js.map +1 -1
  50. package/lib/services/command/context.js +82 -20
  51. package/lib/services/command/context.js.map +1 -1
  52. package/lib/services/config/config.js +4 -7
  53. package/lib/services/config/config.js.map +1 -1
  54. package/lib/services/config/env.js +1 -1
  55. package/lib/services/config/env.js.map +1 -1
  56. package/lib/services/filesync/changes.js +76 -37
  57. package/lib/services/filesync/changes.js.map +1 -1
  58. package/lib/services/filesync/conflicts.js +10 -9
  59. package/lib/services/filesync/conflicts.js.map +1 -1
  60. package/lib/services/filesync/directory.js +16 -1
  61. package/lib/services/filesync/directory.js.map +1 -1
  62. package/lib/services/filesync/error.js +96 -27
  63. package/lib/services/filesync/error.js.map +1 -1
  64. package/lib/services/filesync/filesync.js +448 -490
  65. package/lib/services/filesync/filesync.js.map +1 -1
  66. package/lib/services/filesync/hashes.js +8 -5
  67. package/lib/services/filesync/hashes.js.map +1 -1
  68. package/lib/services/filesync/strategy.js +59 -0
  69. package/lib/services/filesync/strategy.js.map +1 -0
  70. package/lib/services/filesync/sync-json.js +475 -0
  71. package/lib/services/filesync/sync-json.js.map +1 -0
  72. package/lib/services/http/auth.js +30 -1
  73. package/lib/services/http/auth.js.map +1 -1
  74. package/lib/services/http/http.js +5 -0
  75. package/lib/services/http/http.js.map +1 -1
  76. package/lib/services/output/confirm.js +149 -0
  77. package/lib/services/output/confirm.js.map +1 -0
  78. package/lib/services/output/footer.js +22 -0
  79. package/lib/services/output/footer.js.map +1 -0
  80. package/lib/services/output/log/format/pretty.js +2 -1
  81. package/lib/services/output/log/format/pretty.js.map +1 -1
  82. package/lib/services/output/log/logger.js +13 -5
  83. package/lib/services/output/log/logger.js.map +1 -1
  84. package/lib/services/output/log/structured.js +2 -2
  85. package/lib/services/output/log/structured.js.map +1 -1
  86. package/lib/services/output/output.js +197 -0
  87. package/lib/services/output/output.js.map +1 -0
  88. package/lib/services/output/print.js +31 -0
  89. package/lib/services/output/print.js.map +1 -0
  90. package/lib/services/output/problems.js +84 -0
  91. package/lib/services/output/problems.js.map +1 -0
  92. package/lib/services/output/prompt.js +173 -40
  93. package/lib/services/output/prompt.js.map +1 -1
  94. package/lib/services/output/report.js +63 -19
  95. package/lib/services/output/report.js.map +1 -1
  96. package/lib/services/output/select.js +198 -0
  97. package/lib/services/output/select.js.map +1 -0
  98. package/lib/services/output/spinner.js +141 -0
  99. package/lib/services/output/spinner.js.map +1 -0
  100. package/lib/services/output/sprint.js +38 -15
  101. package/lib/services/output/sprint.js.map +1 -1
  102. package/lib/services/output/symbols.js +23 -0
  103. package/lib/services/output/symbols.js.map +1 -0
  104. package/lib/services/output/table.js +98 -0
  105. package/lib/services/output/table.js.map +1 -0
  106. package/lib/services/output/timestamp.js +12 -0
  107. package/lib/services/output/timestamp.js.map +1 -0
  108. package/lib/services/output/update.js +29 -9
  109. package/lib/services/output/update.js.map +1 -1
  110. package/lib/services/user/session.js +4 -0
  111. package/lib/services/user/session.js.map +1 -1
  112. package/lib/services/user/user.js +15 -10
  113. package/lib/services/user/user.js.map +1 -1
  114. package/lib/services/util/assert.js +11 -0
  115. package/lib/services/util/assert.js.map +1 -0
  116. package/lib/services/util/boolean.js +2 -2
  117. package/lib/services/util/boolean.js.map +1 -1
  118. package/lib/services/util/function.js +45 -7
  119. package/lib/services/util/function.js.map +1 -1
  120. package/lib/services/util/is.js +23 -2
  121. package/lib/services/util/is.js.map +1 -1
  122. package/lib/services/util/json.js +16 -13
  123. package/lib/services/util/json.js.map +1 -1
  124. package/lib/services/util/object.js +2 -2
  125. package/lib/services/util/object.js.map +1 -1
  126. package/lib/services/util/promise.js +5 -2
  127. package/lib/services/util/promise.js.map +1 -1
  128. package/lib/services/util/types.js.map +1 -1
  129. package/npm-shrinkwrap.json +3415 -2973
  130. package/package.json +47 -40
  131. package/bin/dev.cmd +0 -3
  132. package/bin/dev.js +0 -14
  133. package/bin/run.cmd +0 -3
  134. package/bin/run.js +0 -5
  135. package/lib/commands/sync.js +0 -284
  136. package/lib/commands/sync.js.map +0 -1
  137. package/lib/services/app/edit/client.js.map +0 -1
  138. package/lib/services/app/edit/error.js.map +0 -1
  139. package/lib/services/output/log/printer.js +0 -120
  140. package/lib/services/output/log/printer.js.map +0 -1
  141. package/lib/services/output/stream.js +0 -54
  142. 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.4.10",
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/main.js",
18
+ "main": "lib/ggt.js",
19
19
  "bin": {
20
- "ggt": "bin/run.js"
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.93.0",
48
- "@swc/helpers": "^0.5.3",
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
- "find-up": "^6.3.0",
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.14.3",
64
- "ignore": "^5.3.0",
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-fn": "^5.0.0",
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": "^9.1.0",
72
- "ora": "^7.0.1",
73
- "p-map": "^6.0.0",
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
- "prompts": "^2.4.2",
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.0",
91
- "@graphql-codegen/cli": "^5.0.0",
92
- "@graphql-codegen/typescript": "^4.0.0",
93
- "@graphql-codegen/typescript-operations": "^4.0.1",
94
- "@swc-node/register": "^1.6.8",
95
- "@swc/cli": "^0.1.63",
96
- "@swc/core": "^1.3.103",
97
- "@types/eslint": "^8.56.2",
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.7",
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": "^6.18.1",
109
- "@typescript-eslint/parser": "^6.19.0",
116
+ "@typescript-eslint/eslint-plugin": "^7.1.1",
117
+ "@typescript-eslint/parser": "^7.1.1",
110
118
  "concurrently": "^8.2.2",
111
- "cspell": "^7.3.8",
112
- "eslint": "^8.56.0",
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": "^47.0.2",
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": "^50.0.1",
120
- "nock": "^13.5.0",
121
- "prettier": "^3.1.1",
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.9",
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.9.0",
129
- "typescript": "^5.3.3",
130
- "vitest": "^0.34.6"
135
+ "type-fest": "^4.12.0",
136
+ "typescript": "^5.4.2",
137
+ "vitest": "^1.3.1"
131
138
  },
132
139
  "engines": {
133
- "node": ">=16.0.0"
140
+ "node": ">=18.0.0"
134
141
  }
135
142
  }
package/bin/dev.cmd DELETED
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node "%~dp0\dev" %*
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
@@ -1,3 +0,0 @@
1
- @echo off
2
-
3
- node "%~dp0\run" %*
package/bin/run.js DELETED
@@ -1,5 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { ggt } from "../lib/ggt.js";
4
-
5
- await ggt();
@@ -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
@@ -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":";AACA,SAASA,YAAY,QAAQ,aAAa;AAC1C,OAAOC,YAAY,cAAc;AAEjC,OAAOC,YAAY,UAAU;AAE7B,OAAOC,eAAe,KAAK;AAE3B,SAASC,MAAM,QAAQ,yBAAyB;AAChD,SAASC,UAAU,QAAQ,qBAAqB;AAChD,SAASC,IAAI,QAA0B,qBAAqB;AAC5D,SAASC,IAAI,EAAEC,OAAO,QAAoB,yBAAyB;AACnE,SAASC,QAAQ,QAAQ,mBAAmB;AAC5C,SAASC,SAAS,QAAQ,aAAa;;UAGlCC;;;;GAAAA,qBAAAA;AAML;;;CAGC,GACD,OAAO,MAAMC;IA6EX;;GAEC,GACDC,UACEC,GAAY,EACZ,EACEC,YAAY,EACZC,SAAS,EACTC,UAAU,EACVC,SAASC,cAAc,EACvBC,aAAab,IAAI,EAOlB,EACW;QACZ,IAAIc,UAAU;YAAEC,OAAOP;YAAcC,WAAWR,QAAQQ;QAAW;QAEnE,MAAMO,0BAA0B,IAAI,CAACC,gBAAgB,CAACC,EAAE,CAAC,aAAa;YACpE,IAAI,IAAI,CAACC,MAAM,QAAoC;gBACjDL,UAAU;oBAAEC,OAAOP;oBAAcC,WAAWR,QAAQQ;gBAAW;gBAC/DF,IAAIa,GAAG,CAACC,IAAI,CAAC;YACf;QACF;QAEA,MAAMC,QAAQ,IAAI3B,OAAO;YAAE4B,aAAa;QAAE;QAC1C,MAAMZ,UAAU,CAACa,QAAqCZ,eAAe,IAAIT,UAAUK,cAAcgB;QAEjG,MAAMC,cAAc,IAAI,CAACR,gBAAgB,CAACX,SAAS,CAAmDQ,SAAS;YAC7G,kEAAkE;YAClEY,MAAM,CAACC,WAAaL,MAAMM,GAAG,CAAC,IAAMlB,WAAWiB,WAAWE,KAAK,CAAClB;YAChE,kEAAkE;YAClEa,OAAO,CAACA,QAAUF,MAAMM,GAAG,CAAC,IAAMjB,QAAQa;YAC1C,kEAAkE;YAClEM,UAAU,IAAMR,MAAMM,GAAG,CAAC,IAAMf,cAAcgB,KAAK,CAAClB;QACtD;QAEA,OAAO;YACLK;YACAS;QACF;IACF;IAEA;;GAEC,GACD,MAAMM,QACJxB,GAAY,EACZO,OAIC,EACqE;QACtEpB,OAAOa,IAAIyB,GAAG,EAAE;QAEhB,MAAMC,SAASnC;QACfJ,OAAOuC,QAAQ;QAEf,IAAIC,YAAY3B,IAAIyB,GAAG,CAACG,IAAI;QAC5B,IAAI5B,IAAIyB,GAAG,CAACI,oBAAoB,EAAE;YAChCF,aAAa;QACf;QAEA,IAAI;YACF,MAAMG,OAAO,MAAMtC,KAAK;gBACtBuC,SAAS;oBAAE/B;gBAAI;gBACfgC,QAAQ;gBACRC,KAAK,CAAC,QAAQ,EAAEN,UAAU,CAAC,EAAErC,OAAO4C,OAAO,CAACT,GAAG,CAAC,iBAAiB,CAAC;gBAClEU,SAAS;oBAAET;gBAAO;gBAClBI,MAAM;oBAAEtB,OAAOD,QAAQ6B,SAAS;oBAAElC,WAAWR,QAAQa,QAAQL,SAAS;gBAAE;gBACxEmC,cAAc;gBACdC,iBAAiB;gBACjBC,iBAAiB;gBACjB,GAAGhC,QAAQf,IAAI;YACjB;YAEA,IAAI,CAACG,SAASmC,SAAU,CAAE,CAAA,UAAUA,IAAG,KAAM,CAAE,CAAA,YAAYA,IAAG,GAAK;gBACjE9B,IAAIa,GAAG,CAACI,KAAK,CAAC,qCAAqC;oBAAEA,OAAOa;gBAAK;gBACjE,MAAMA;YACR;YAEA,OAAOA;QACT,EAAE,OAAOb,OAAO;YACd,MAAM,IAAIrB,UAAUW,QAAQ6B,SAAS,EAAEnB;QACzC;IACF;IAEA;;GAEC,GACD,MAAMuB,UAAyB;QAC7B,MAAM,IAAI,CAAC9B,gBAAgB,CAAC8B,OAAO;IACrC;IArKAC,YAAYzC,GAAY,CAAE;QAP1B,wCAAwC;QACxCY,uBAAAA;QAEA,uBAASZ,OAAT,KAAA;QAEA,uBAAQU,oBAAR,KAAA;QAGE,IAAI,CAACV,GAAG,GAAGA,IAAI0C,KAAK,CAAC;YAAEC,MAAM;QAAS;QACtCxD,OAAOa,IAAIyB,GAAG,EAAE;QAEhB,IAAIE,YAAY3B,IAAIyB,GAAG,CAACG,IAAI;QAC5B,IAAI5B,IAAIyB,GAAG,CAACI,oBAAoB,EAAE;YAChCF,aAAa;QACf;QAEA,IAAI,CAACjB,gBAAgB,GAAGxB,aAAa;YACnC+C,KAAK,CAAC,MAAM,EAAEN,UAAU,CAAC,EAAErC,OAAO4C,OAAO,CAACT,GAAG,CAAC,oBAAoB,CAAC;YACnEmB,aAAa,IAAM;YACnBC,eAAe,cAAcxD;gBAC3BoD,YAAYK,OAAqB,EAAEC,SAA6B,EAAEC,SAAuD,CAAE;oBACzH,6FAA6F;oBAC7F,MAAMtB,SAASnC;oBACfJ,OAAOuC,QAAQ;oBAEf,KAAK,CAACoB,SAASC,WAAW;wBACxBE,QAAQjD,IAAIiD,MAAM;wBAClB,GAAGD,SAAS;wBACZb,SAAS;4BACP,GAAGa,WAAWb,OAAO;4BACrB,cAAc7C,OAAO4D,WAAW;4BAChCxB;wBACF;oBACF;gBACF;YACF;YACAf,IAAI;gBACFwC,YAAY;oBACV,OAAQ,IAAI,CAACvC,MAAM;wBACjB;4BACE,IAAI,CAACA,MAAM;4BACX,IAAI,CAACZ,GAAG,CAACa,GAAG,CAACC,IAAI,CAAC;4BAClB;wBACF;4BACE,IAAI,CAACd,GAAG,CAACa,GAAG,CAACC,IAAI,CAAC;4BAClB;wBACF;4BACE,IAAI,CAACd,GAAG,CAACa,GAAG,CAACuC,KAAK,CAAC;4BACnB;oBACJ;gBACF;gBACAC,WAAW;oBACT,IAAI,IAAI,CAACzC,MAAM,QAAoC;wBACjD,IAAI,CAACZ,GAAG,CAACa,GAAG,CAACC,IAAI,CAAC;oBACpB,OAAO;wBACL,IAAI,CAACd,GAAG,CAACa,GAAG,CAACuC,KAAK,CAAC;oBACrB;oBAEA,gEAAgE;oBAChEE,aAAa,IAAO,IAAI,CAAC1C,MAAM;gBACjC;gBACA2C,QAAQ;oBACN,IAAI,CAAC3C,MAAM;oBACX,IAAI,CAACZ,GAAG,CAACa,GAAG,CAACuC,KAAK,CAAC;gBACrB;gBACAnC,OAAO,CAACA;oBACN,IAAI,IAAI,CAACL,MAAM,QAAoC;wBACjD,IAAI,CAACZ,GAAG,CAACa,GAAG,CAACI,KAAK,CAAC,uBAAuB;4BAAEA;wBAAM;oBACpD,OAAO;wBACL,IAAI,CAACjB,GAAG,CAACa,GAAG,CAACI,KAAK,CAAC,oBAAoB;4BAAEA;wBAAM;oBACjD;gBACF;YACF;QACF;IACF;AAmGF"}
@@ -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"}