@gadgetinc/ggt 1.0.3 → 1.0.5

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 (220) hide show
  1. package/dist/add-4ME2KTJQ.js +84 -0
  2. package/dist/add-4ME2KTJQ.js.map +7 -0
  3. package/dist/build-4BGA4IZE.js +3 -0
  4. package/dist/build-4BGA4IZE.js.map +7 -0
  5. package/dist/chunk-57XALM2W.js +2 -0
  6. package/dist/chunk-57XALM2W.js.map +7 -0
  7. package/dist/chunk-5DLLJX5F.js +11 -0
  8. package/dist/chunk-5DLLJX5F.js.map +7 -0
  9. package/dist/chunk-5WC5D4WL.js +2 -0
  10. package/dist/chunk-5WC5D4WL.js.map +7 -0
  11. package/dist/chunk-BSCZOMV4.js +2 -0
  12. package/dist/chunk-BSCZOMV4.js.map +7 -0
  13. package/dist/chunk-BZY2AIPV.js +57 -0
  14. package/dist/chunk-BZY2AIPV.js.map +7 -0
  15. package/dist/chunk-D2K5XPNJ.js +2 -0
  16. package/dist/chunk-D2K5XPNJ.js.map +7 -0
  17. package/dist/chunk-F3EZ4KS3.js +2 -0
  18. package/dist/chunk-F3EZ4KS3.js.map +7 -0
  19. package/dist/chunk-G3VNV5Z6.js +33 -0
  20. package/dist/chunk-G3VNV5Z6.js.map +7 -0
  21. package/dist/chunk-GA7AL463.js +24 -0
  22. package/dist/chunk-GA7AL463.js.map +7 -0
  23. package/dist/chunk-GOBNB5VT.js +2 -0
  24. package/dist/chunk-GOBNB5VT.js.map +7 -0
  25. package/dist/chunk-HKBXEZNF.js +2 -0
  26. package/dist/chunk-HKBXEZNF.js.map +7 -0
  27. package/{lib/commands/push.js → dist/chunk-IKJVXTZK.js} +7 -48
  28. package/dist/chunk-IKJVXTZK.js.map +7 -0
  29. package/dist/chunk-KGSSHEEC.js +17 -0
  30. package/dist/chunk-KGSSHEEC.js.map +7 -0
  31. package/dist/chunk-KXK37C5D.js +183 -0
  32. package/dist/chunk-KXK37C5D.js.map +7 -0
  33. package/dist/chunk-NJIDVM2C.js +27 -0
  34. package/dist/chunk-NJIDVM2C.js.map +7 -0
  35. package/dist/chunk-Q5N5L6H3.js +6 -0
  36. package/dist/chunk-Q5N5L6H3.js.map +7 -0
  37. package/dist/chunk-QEVWPU3D.js +8 -0
  38. package/dist/chunk-QEVWPU3D.js.map +7 -0
  39. package/dist/chunk-X36GM74C.js +2 -0
  40. package/dist/chunk-X36GM74C.js.map +7 -0
  41. package/dist/chunk-YRP2UZ2I.js +126 -0
  42. package/dist/chunk-YRP2UZ2I.js.map +7 -0
  43. package/dist/deploy-6HOBYPE6.js +30 -0
  44. package/dist/deploy-6HOBYPE6.js.map +7 -0
  45. package/dist/dev-M3WDGK4D.js +57 -0
  46. package/dist/dev-M3WDGK4D.js.map +7 -0
  47. package/dist/esm-BL3OM5UJ.js +33 -0
  48. package/dist/esm-BL3OM5UJ.js.map +7 -0
  49. package/dist/list-725RHWD5.js +11 -0
  50. package/dist/list-725RHWD5.js.map +7 -0
  51. package/dist/login-I4RGMDLE.js +2 -0
  52. package/dist/login-I4RGMDLE.js.map +7 -0
  53. package/dist/logout-UZBACOIX.js +7 -0
  54. package/dist/logout-UZBACOIX.js.map +7 -0
  55. package/dist/main.js +45 -0
  56. package/dist/main.js.map +7 -0
  57. package/dist/open-GWKGQQG3.js +74 -0
  58. package/dist/open-GWKGQQG3.js.map +7 -0
  59. package/{lib/commands/pull.js → dist/pull-7C5A65KB.js} +7 -48
  60. package/dist/pull-7C5A65KB.js.map +7 -0
  61. package/dist/push-LPUIR4EO.js +2 -0
  62. package/dist/push-LPUIR4EO.js.map +7 -0
  63. package/dist/status-DFKKSRVB.js +14 -0
  64. package/dist/status-DFKKSRVB.js.map +7 -0
  65. package/dist/version-JD42JXWY.js +11 -0
  66. package/dist/version-JD42JXWY.js.map +7 -0
  67. package/dist/whoami-I23R6HOG.js +7 -0
  68. package/dist/whoami-I23R6HOG.js.map +7 -0
  69. package/package.json +73 -75
  70. package/lib/__generated__/graphql.js +0 -78
  71. package/lib/__generated__/graphql.js.map +0 -1
  72. package/lib/commands/add.js +0 -385
  73. package/lib/commands/add.js.map +0 -1
  74. package/lib/commands/deploy.js +0 -287
  75. package/lib/commands/deploy.js.map +0 -1
  76. package/lib/commands/dev.js +0 -374
  77. package/lib/commands/dev.js.map +0 -1
  78. package/lib/commands/list.js +0 -47
  79. package/lib/commands/list.js.map +0 -1
  80. package/lib/commands/login.js +0 -93
  81. package/lib/commands/login.js.map +0 -1
  82. package/lib/commands/logout.js +0 -20
  83. package/lib/commands/logout.js.map +0 -1
  84. package/lib/commands/open.js +0 -159
  85. package/lib/commands/open.js.map +0 -1
  86. package/lib/commands/pull.js.map +0 -1
  87. package/lib/commands/push.js.map +0 -1
  88. package/lib/commands/root.js +0 -110
  89. package/lib/commands/root.js.map +0 -1
  90. package/lib/commands/status.js +0 -56
  91. package/lib/commands/status.js.map +0 -1
  92. package/lib/commands/version.js +0 -18
  93. package/lib/commands/version.js.map +0 -1
  94. package/lib/commands/whoami.js +0 -23
  95. package/lib/commands/whoami.js.map +0 -1
  96. package/lib/ggt.js +0 -65
  97. package/lib/ggt.js.map +0 -1
  98. package/lib/main.js +0 -5
  99. package/lib/main.js.map +0 -1
  100. package/lib/services/app/api/api.js +0 -191
  101. package/lib/services/app/api/api.js.map +0 -1
  102. package/lib/services/app/api/operation.js +0 -23
  103. package/lib/services/app/api/operation.js.map +0 -1
  104. package/lib/services/app/app.js +0 -95
  105. package/lib/services/app/app.js.map +0 -1
  106. package/lib/services/app/arg.js +0 -28
  107. package/lib/services/app/arg.js.map +0 -1
  108. package/lib/services/app/client.js +0 -182
  109. package/lib/services/app/client.js.map +0 -1
  110. package/lib/services/app/edit/edit.js +0 -191
  111. package/lib/services/app/edit/edit.js.map +0 -1
  112. package/lib/services/app/edit/operation.js +0 -155
  113. package/lib/services/app/edit/operation.js.map +0 -1
  114. package/lib/services/app/error.js +0 -65
  115. package/lib/services/app/error.js.map +0 -1
  116. package/lib/services/command/arg.js +0 -55
  117. package/lib/services/command/arg.js.map +0 -1
  118. package/lib/services/command/command.js +0 -47
  119. package/lib/services/command/command.js.map +0 -1
  120. package/lib/services/command/context.js +0 -209
  121. package/lib/services/command/context.js.map +0 -1
  122. package/lib/services/config/config.js +0 -133
  123. package/lib/services/config/config.js.map +0 -1
  124. package/lib/services/config/env.js +0 -22
  125. package/lib/services/config/env.js.map +0 -1
  126. package/lib/services/config/package-json.js +0 -9
  127. package/lib/services/config/package-json.js.map +0 -1
  128. package/lib/services/filesync/changes.js +0 -134
  129. package/lib/services/filesync/changes.js.map +0 -1
  130. package/lib/services/filesync/conflicts.js +0 -140
  131. package/lib/services/filesync/conflicts.js.map +0 -1
  132. package/lib/services/filesync/directory.js +0 -269
  133. package/lib/services/filesync/directory.js.map +0 -1
  134. package/lib/services/filesync/error.js +0 -137
  135. package/lib/services/filesync/error.js.map +0 -1
  136. package/lib/services/filesync/file.js +0 -3
  137. package/lib/services/filesync/file.js.map +0 -1
  138. package/lib/services/filesync/filesync.js +0 -791
  139. package/lib/services/filesync/filesync.js.map +0 -1
  140. package/lib/services/filesync/hashes.js +0 -172
  141. package/lib/services/filesync/hashes.js.map +0 -1
  142. package/lib/services/filesync/strategy.js +0 -59
  143. package/lib/services/filesync/strategy.js.map +0 -1
  144. package/lib/services/filesync/sync-json.js +0 -475
  145. package/lib/services/filesync/sync-json.js.map +0 -1
  146. package/lib/services/http/auth.js +0 -70
  147. package/lib/services/http/auth.js.map +0 -1
  148. package/lib/services/http/http.js +0 -136
  149. package/lib/services/http/http.js.map +0 -1
  150. package/lib/services/output/confirm.js +0 -149
  151. package/lib/services/output/confirm.js.map +0 -1
  152. package/lib/services/output/footer.js +0 -22
  153. package/lib/services/output/footer.js.map +0 -1
  154. package/lib/services/output/log/field.js +0 -3
  155. package/lib/services/output/log/field.js.map +0 -1
  156. package/lib/services/output/log/format/format.js +0 -8
  157. package/lib/services/output/log/format/format.js.map +0 -1
  158. package/lib/services/output/log/format/json.js +0 -37
  159. package/lib/services/output/log/format/json.js.map +0 -1
  160. package/lib/services/output/log/format/pretty.js +0 -158
  161. package/lib/services/output/log/format/pretty.js.map +0 -1
  162. package/lib/services/output/log/level.js +0 -41
  163. package/lib/services/output/log/level.js.map +0 -1
  164. package/lib/services/output/log/logger.js +0 -53
  165. package/lib/services/output/log/logger.js.map +0 -1
  166. package/lib/services/output/log/structured.js +0 -52
  167. package/lib/services/output/log/structured.js.map +0 -1
  168. package/lib/services/output/notify.js +0 -27
  169. package/lib/services/output/notify.js.map +0 -1
  170. package/lib/services/output/output.js +0 -197
  171. package/lib/services/output/output.js.map +0 -1
  172. package/lib/services/output/print.js +0 -31
  173. package/lib/services/output/print.js.map +0 -1
  174. package/lib/services/output/problems.js +0 -84
  175. package/lib/services/output/problems.js.map +0 -1
  176. package/lib/services/output/prompt.js +0 -185
  177. package/lib/services/output/prompt.js.map +0 -1
  178. package/lib/services/output/report.js +0 -190
  179. package/lib/services/output/report.js.map +0 -1
  180. package/lib/services/output/select.js +0 -198
  181. package/lib/services/output/select.js.map +0 -1
  182. package/lib/services/output/spinner.js +0 -141
  183. package/lib/services/output/spinner.js.map +0 -1
  184. package/lib/services/output/sprint.js +0 -44
  185. package/lib/services/output/sprint.js.map +0 -1
  186. package/lib/services/output/symbols.js +0 -23
  187. package/lib/services/output/symbols.js.map +0 -1
  188. package/lib/services/output/table.js +0 -98
  189. package/lib/services/output/table.js.map +0 -1
  190. package/lib/services/output/timestamp.js +0 -12
  191. package/lib/services/output/timestamp.js.map +0 -1
  192. package/lib/services/output/update.js +0 -99
  193. package/lib/services/output/update.js.map +0 -1
  194. package/lib/services/user/session.js +0 -54
  195. package/lib/services/user/session.js.map +0 -1
  196. package/lib/services/user/user.js +0 -76
  197. package/lib/services/user/user.js.map +0 -1
  198. package/lib/services/util/assert.js +0 -11
  199. package/lib/services/util/assert.js.map +0 -1
  200. package/lib/services/util/boolean.js +0 -15
  201. package/lib/services/util/boolean.js.map +0 -1
  202. package/lib/services/util/collection.js +0 -38
  203. package/lib/services/util/collection.js.map +0 -1
  204. package/lib/services/util/function.js +0 -153
  205. package/lib/services/util/function.js.map +0 -1
  206. package/lib/services/util/is.js +0 -97
  207. package/lib/services/util/is.js.map +0 -1
  208. package/lib/services/util/json.js +0 -33
  209. package/lib/services/util/json.js.map +0 -1
  210. package/lib/services/util/number.js +0 -27
  211. package/lib/services/util/number.js.map +0 -1
  212. package/lib/services/util/object.js +0 -104
  213. package/lib/services/util/object.js.map +0 -1
  214. package/lib/services/util/paths.js +0 -36
  215. package/lib/services/util/paths.js.map +0 -1
  216. package/lib/services/util/promise.js +0 -74
  217. package/lib/services/util/promise.js.map +0 -1
  218. package/lib/services/util/types.js +0 -5
  219. package/lib/services/util/types.js.map +0 -1
  220. package/npm-shrinkwrap.json +0 -29512
@@ -1,791 +0,0 @@
1
- import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
- import { execa } from "execa";
3
- import fs from "fs-extra";
4
- import ms from "ms";
5
- import assert from "node:assert";
6
- import path from "node:path";
7
- import process from "node:process";
8
- import pMap from "p-map";
9
- import PQueue from "p-queue";
10
- import pRetry from "p-retry";
11
- import pluralize from "pluralize";
12
- import { FileSyncEncoding } from "../../__generated__/graphql.js";
13
- import { FILE_SYNC_COMPARISON_HASHES_QUERY, FILE_SYNC_FILES_QUERY, FILE_SYNC_HASHES_QUERY, PUBLISH_FILE_SYNC_EVENTS_MUTATION, REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION } from "../app/edit/operation.js";
14
- import { config } from "../config/config.js";
15
- import { confirm } from "../output/confirm.js";
16
- import { println } from "../output/print.js";
17
- import { filesyncProblemsToProblems, sprintProblems } from "../output/problems.js";
18
- import { EdgeCaseError } from "../output/report.js";
19
- import { select } from "../output/select.js";
20
- import { spin } from "../output/spinner.js";
21
- import { sprint, sprintln } from "../output/sprint.js";
22
- import { symbol } from "../output/symbols.js";
23
- import { ts } from "../output/timestamp.js";
24
- import { noop } from "../util/function.js";
25
- import { isEEXISTError, isENOENTError, isENOTDIRError, isENOTEMPTYError } from "../util/is.js";
26
- import { serializeError } from "../util/object.js";
27
- import { Changes, printChanges, sprintChanges } from "./changes.js";
28
- import { getConflicts, printConflicts, withoutConflictingChanges } from "./conflicts.js";
29
- import { supportsPermissions, swallowEnoent } from "./directory.js";
30
- import { TooManyMergeAttemptsError, isFilesVersionMismatchError, swallowFilesVersionMismatch } from "./error.js";
31
- import { getNecessaryChanges, isEqualHashes } from "./hashes.js";
32
- import { MergeConflictPreference } from "./strategy.js";
33
- /**
34
- * The maximum attempts to automatically merge local and environment
35
- * file changes when a FilesVersionMismatchError is encountered before
36
- * throwing a {@linkcode TooManyMergeAttemptsError}.
37
- */ export const MAX_MERGE_ATTEMPTS = 10;
38
- /**
39
- * The maximum length of file content that can be pushed to Gadget in a
40
- * single request.
41
- */ export const MAX_PUSH_CONTENT_LENGTH = 50 * 1024 * 1024; // 50mb
42
- export class FileSync {
43
- async hashes(ctx, quietly) {
44
- const spinner = !quietly ? spin({
45
- ensureEmptyLineAbove: true
46
- })`
47
- Calculating file changes.
48
- ` : undefined;
49
- try {
50
- const [localHashes, { localFilesVersionHashes, environmentHashes, environmentFilesVersion }] = await Promise.all([
51
- // get the hashes of our local files
52
- this.syncJson.directory.hashes(),
53
- // get the hashes of our local filesVersion and the latest filesVersion
54
- (async ()=>{
55
- let localFilesVersionHashes;
56
- let environmentHashes;
57
- let environmentFilesVersion;
58
- if (this.syncJson.filesVersion === 0n) {
59
- // we're either syncing for the first time or we're syncing a
60
- // non-empty directory without a `.gadget/sync.json` file,
61
- // regardless get the hashes of the latest filesVersion
62
- const { fileSyncHashes } = await this.syncJson.edit.query({
63
- query: FILE_SYNC_HASHES_QUERY
64
- });
65
- environmentFilesVersion = BigInt(fileSyncHashes.filesVersion);
66
- environmentHashes = fileSyncHashes.hashes;
67
- localFilesVersionHashes = {}; // represents an empty directory
68
- } else {
69
- // this isn't the first time we're syncing, so get the
70
- // hashes of the files at our local filesVersion and the
71
- // latest filesVersion
72
- const { fileSyncComparisonHashes } = await this.syncJson.edit.query({
73
- query: FILE_SYNC_COMPARISON_HASHES_QUERY,
74
- variables: {
75
- filesVersion: String(this.syncJson.filesVersion)
76
- }
77
- });
78
- localFilesVersionHashes = fileSyncComparisonHashes.filesVersionHashes.hashes;
79
- environmentHashes = fileSyncComparisonHashes.latestFilesVersionHashes.hashes;
80
- environmentFilesVersion = BigInt(fileSyncComparisonHashes.latestFilesVersionHashes.filesVersion);
81
- }
82
- return {
83
- localFilesVersionHashes,
84
- environmentHashes,
85
- environmentFilesVersion
86
- };
87
- })()
88
- ]);
89
- const inSync = isEqualHashes(ctx, localHashes, environmentHashes);
90
- const localChanges = getNecessaryChanges(ctx, {
91
- from: localFilesVersionHashes,
92
- to: localHashes,
93
- existing: environmentHashes,
94
- ignore: [
95
- ".gadget/"
96
- ]
97
- });
98
- let environmentChanges = getNecessaryChanges(ctx, {
99
- from: localFilesVersionHashes,
100
- to: environmentHashes,
101
- existing: localHashes
102
- });
103
- if (!inSync && localChanges.size === 0 && environmentChanges.size === 0) {
104
- // we're not in sync, but neither the local filesystem nor the
105
- // environment's filesystem have any changes; this is only
106
- // possible if the local filesystem has modified .gadget/ files
107
- environmentChanges = getNecessaryChanges(ctx, {
108
- from: localHashes,
109
- to: environmentHashes
110
- });
111
- assert(environmentChanges.size > 0, "expected environmentChanges to have changes");
112
- assert(Array.from(environmentChanges.keys()).every((path)=>path.startsWith(".gadget/")), "expected all environmentChanges to be .gadget/ files");
113
- }
114
- assert(inSync || localChanges.size > 0 || environmentChanges.size > 0, "there must be changes if hashes don't match");
115
- const localChangesToPush = getNecessaryChanges(ctx, {
116
- from: environmentHashes,
117
- to: localHashes,
118
- ignore: [
119
- ".gadget/"
120
- ]
121
- });
122
- const environmentChangesToPull = getNecessaryChanges(ctx, {
123
- from: localHashes,
124
- to: environmentHashes
125
- });
126
- const onlyDotGadgetFilesChanged = Array.from(environmentChangesToPull.keys()).every((filepath)=>filepath.startsWith(".gadget/"));
127
- const bothChanged = localChanges.size > 0 && environmentChanges.size > 0 && !onlyDotGadgetFilesChanged;
128
- if (spinner) {
129
- if (inSync) {
130
- spinner.succeed`Your files are up to date. ${ts()}`;
131
- } else {
132
- spinner.succeed`Calculated file changes. ${ts()}`;
133
- }
134
- }
135
- return {
136
- inSync,
137
- localFilesVersionHashes,
138
- localHashes,
139
- localChanges,
140
- localChangesToPush,
141
- environmentHashes,
142
- environmentChanges,
143
- environmentChangesToPull,
144
- environmentFilesVersion,
145
- onlyDotGadgetFilesChanged,
146
- bothChanged
147
- };
148
- } catch (error) {
149
- if (spinner) {
150
- spinner.fail();
151
- }
152
- throw error;
153
- }
154
- }
155
- async print(ctx, { hashes } = {}) {
156
- const { inSync, localChanges, environmentChanges, onlyDotGadgetFilesChanged, bothChanged } = hashes ?? await this.hashes(ctx);
157
- if (inSync) {
158
- // the spinner in hashes will have already printed that we're in sync
159
- return;
160
- }
161
- if (localChanges.size > 0) {
162
- printChanges(ctx, {
163
- changes: localChanges,
164
- tense: "past",
165
- title: sprint`Your local files {underline have} changed.`
166
- });
167
- } else {
168
- println({
169
- ensureEmptyLineAbove: true
170
- })`
171
- Your local files {underline have not} changed.
172
- `;
173
- }
174
- if (environmentChanges.size > 0 && !onlyDotGadgetFilesChanged) {
175
- printChanges(ctx, {
176
- changes: environmentChanges,
177
- tense: "past",
178
- title: sprint`Your environment's files {underline have}${bothChanged ? " also" : ""} changed.`
179
- });
180
- } else {
181
- println({
182
- ensureEmptyLineAbove: true
183
- })`
184
- Your environment's files {underline have not} changed.
185
- `;
186
- }
187
- }
188
- /**
189
- * Waits for all pending and ongoing filesync operations to complete.
190
- */ async idle() {
191
- await this._syncOperations.onIdle();
192
- }
193
- /**
194
- * Attempts to send file changes to the Gadget. If a files version
195
- * mismatch error occurs, this function will merge the changes with
196
- * Gadget instead.
197
- *
198
- * @param ctx - The context to use.
199
- * @param options - The options to use.
200
- * @param options.changes - The changes to send.
201
- * @param options.printLocalChangesOptions - The options to use when printing the local changes.
202
- * @param options.printEnvironmentChangesOptions - The options to use when printing the changes from Gadget.
203
- * @returns A promise that resolves when the changes have been sent.
204
- */ async mergeChangesWithEnvironment(ctx, { changes, printLocalChangesOptions, printEnvironmentChangesOptions }) {
205
- await this._syncOperations.add(async ()=>{
206
- try {
207
- await this._sendChangesToEnvironment(ctx, {
208
- changes,
209
- printLocalChangesOptions
210
- });
211
- } catch (error) {
212
- swallowFilesVersionMismatch(ctx, error);
213
- // we either sent the wrong expectedFilesVersion or we received
214
- // a filesVersion that is greater than the expectedFilesVersion
215
- // + 1, so we need to stop what we're doing and get in sync
216
- await this.merge(ctx, {
217
- printEnvironmentChangesOptions
218
- });
219
- }
220
- });
221
- }
222
- /**
223
- * Subscribes to file changes on Gadget and executes the provided
224
- * callbacks before and after the changes occur.
225
- *
226
- * @param ctx - The context to use.
227
- * @param options - The options to use.
228
- * @param options.beforeChanges - A callback that is called before the changes occur.
229
- * @param options.afterChanges - A callback that is called after the changes occur.
230
- * @param options.onError - A callback that is called if an error occurs.
231
- * @param options.printEnvironmentChangesOptions - The options to use when printing the changes from Gadget.
232
- * @returns A function that unsubscribes from changes on Gadget.
233
- */ subscribeToEnvironmentChanges(ctx, { beforeChanges = noop, printEnvironmentChangesOptions, afterChanges = noop, onError }) {
234
- return this.syncJson.edit.subscribe({
235
- subscription: REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,
236
- // the reason this is a function rather than a static value is
237
- // so that it will be re-evaluated if the connection is lost and
238
- // then re-established. this ensures that we send our current
239
- // filesVersion rather than the one that was sent when we first
240
- // subscribed
241
- variables: ()=>({
242
- localFilesVersion: String(this.syncJson.filesVersion)
243
- }),
244
- onError,
245
- onData: ({ remoteFileSyncEvents: { changed, deleted, remoteFilesVersion } })=>{
246
- this._syncOperations.add(async ()=>{
247
- if (BigInt(remoteFilesVersion) < this.syncJson.filesVersion) {
248
- ctx.log.warn("skipping received changes because files version is outdated", {
249
- filesVersion: remoteFilesVersion
250
- });
251
- return;
252
- }
253
- ctx.log.debug("received files", {
254
- remoteFilesVersion,
255
- changed: changed.map((change)=>change.path),
256
- deleted: deleted.map((change)=>change.path)
257
- });
258
- const filterIgnoredFiles = (file)=>{
259
- const ignored = this.syncJson.directory.ignores(file.path);
260
- if (ignored) {
261
- ctx.log.warn("skipping received change because file is ignored", {
262
- path: file.path
263
- });
264
- }
265
- return !ignored;
266
- };
267
- changed = changed.filter(filterIgnoredFiles);
268
- deleted = deleted.filter(filterIgnoredFiles);
269
- if (changed.length === 0 && deleted.length === 0) {
270
- await this.syncJson.save(remoteFilesVersion);
271
- return;
272
- }
273
- await beforeChanges({
274
- changed: changed.map((file)=>file.path),
275
- deleted: deleted.map((file)=>file.path)
276
- });
277
- const changes = await this.writeToLocalFilesystem(ctx, {
278
- filesVersion: remoteFilesVersion,
279
- files: changed,
280
- delete: deleted.map((file)=>file.path),
281
- printEnvironmentChangesOptions: {
282
- tense: "past",
283
- ensureEmptyLineAbove: true,
284
- title: sprintln`{greenBright ${symbol.tick}} Pulled ${pluralize("file", changed.length + deleted.length)}. ${ts()}`,
285
- limit: 5,
286
- ...printEnvironmentChangesOptions
287
- }
288
- });
289
- await afterChanges({
290
- changes
291
- });
292
- }).catch(onError);
293
- }
294
- });
295
- }
296
- /**
297
- * Ensures the local filesystem is in sync with Gadget's filesystem.
298
- * - All non-conflicting changes are automatically merged.
299
- * - Conflicts are resolved by prompting the user to either keep their local changes or keep Gadget's changes.
300
- * - This function will not return until the filesystem is in sync.
301
- */ async merge(ctx, { hashes, maxAttempts = 10, printLocalChangesOptions, printEnvironmentChangesOptions, quietly } = {}) {
302
- let attempt = 0;
303
- do {
304
- if (attempt === 0) {
305
- hashes ??= await this.hashes(ctx, quietly);
306
- } else {
307
- hashes = await this.hashes(ctx, quietly);
308
- }
309
- if (hashes.inSync) {
310
- this._syncOperations.clear();
311
- ctx.log.info("filesystem in sync");
312
- await this.syncJson.save(hashes.environmentFilesVersion);
313
- return;
314
- }
315
- attempt += 1;
316
- ctx.log.info("merging", {
317
- attempt,
318
- ...hashes
319
- });
320
- try {
321
- await this._merge(ctx, {
322
- hashes,
323
- printLocalChangesOptions,
324
- printEnvironmentChangesOptions
325
- });
326
- } catch (error) {
327
- swallowFilesVersionMismatch(ctx, error);
328
- // we either sent the wrong expectedFilesVersion or we received
329
- // a filesVersion that is greater than the expectedFilesVersion
330
- // + 1, so try again
331
- }
332
- }while (attempt < maxAttempts)
333
- throw new TooManyMergeAttemptsError(maxAttempts);
334
- }
335
- /**
336
- * Pushes any changes made to the local filesystem since the last sync
337
- * to Gadget.
338
- *
339
- * If Gadget has also made changes since the last sync, and --force
340
- * was not passed, the user will be prompted to discard them.
341
- */ async push(ctx, { hashes, force, printLocalChangesOptions } = {}) {
342
- const { localChangesToPush, environmentChanges, environmentFilesVersion, onlyDotGadgetFilesChanged } = hashes ?? await this.hashes(ctx);
343
- assert(localChangesToPush.size > 0, "cannot push if there are no changes");
344
- // TODO: lift this check up to the push command
345
- if (// they didn't pass --force
346
- !(force ?? ctx.args["--force"]) && // their environment's files have changed
347
- environmentChanges.size > 0 && // some of the changes aren't .gadget/ files
348
- !onlyDotGadgetFilesChanged) {
349
- await confirm({
350
- ensureEmptyLineAbove: true
351
- })`
352
- Are you sure you want to {underline discard} your environment's changes?
353
- `;
354
- }
355
- try {
356
- await this._sendChangesToEnvironment(ctx, {
357
- // what changes need to be made to your local files to make
358
- // them match the environment's files
359
- changes: localChangesToPush,
360
- expectedFilesVersion: environmentFilesVersion,
361
- printLocalChangesOptions
362
- });
363
- } catch (error) {
364
- swallowFilesVersionMismatch(ctx, error);
365
- // we were told to push their local changes, but their
366
- // environment's files have changed since we last checked, so
367
- // throw a nicer error message
368
- // TODO: we don't have to do this if only .gadget/ files changed
369
- throw new EdgeCaseError(sprint`
370
- Your environment's files have changed since we last checked.
371
-
372
- Please re-run "ggt ${ctx.command}" to see the changes and try again.
373
- `);
374
- }
375
- }
376
- async pull(ctx, { hashes, force, printEnvironmentChangesOptions } = {}) {
377
- const { localChanges, environmentChangesToPull, environmentFilesVersion } = hashes ?? await this.hashes(ctx);
378
- assert(environmentChangesToPull.size > 0, "cannot push if there are no changes");
379
- // TODO: lift this check up to the pull command
380
- if (localChanges.size > 0 && !(force ?? ctx.args["--force"])) {
381
- await confirm`
382
- Are you sure you want to {underline discard} your local changes?
383
- `;
384
- }
385
- await this._getChangesFromEnvironment(ctx, {
386
- changes: environmentChangesToPull,
387
- filesVersion: environmentFilesVersion,
388
- printEnvironmentChangesOptions
389
- });
390
- }
391
- async writeToLocalFilesystem(ctx, options) {
392
- const filesVersion = BigInt(options.filesVersion);
393
- assert(filesVersion >= this.syncJson.filesVersion, "filesVersion must be greater than or equal to current filesVersion");
394
- ctx.log.debug("writing to local filesystem", {
395
- filesVersion,
396
- files: options.files.map((file)=>file.path),
397
- delete: options.delete
398
- });
399
- const changes = new Changes();
400
- const directoriesWithDeletedFiles = new Set();
401
- await pMap(options.delete, async (pathToDelete)=>{
402
- // add all the directories that contain this file to
403
- // directoriesWithDeletedFiles so we can clean them up later
404
- let dir = path.dirname(pathToDelete);
405
- while(dir !== "."){
406
- directoriesWithDeletedFiles.add(this.syncJson.directory.normalize(dir, true));
407
- dir = path.dirname(dir);
408
- }
409
- const currentPath = this.syncJson.directory.absolute(pathToDelete);
410
- const backupPath = this.syncJson.directory.absolute(".gadget/backup", this.syncJson.directory.relative(pathToDelete));
411
- // rather than `rm -rf`ing files, we move them to
412
- // `.gadget/backup/` so that users can recover them if something
413
- // goes wrong. We've seen a lot of EBUSY/EINVAL errors when moving
414
- // files so we retry a few times.
415
- await pRetry(async ()=>{
416
- try {
417
- // remove the current backup file in case it exists and is a
418
- // different type (file vs directory)
419
- await fs.remove(backupPath);
420
- await fs.move(currentPath, backupPath);
421
- changes.set(pathToDelete, {
422
- type: "delete"
423
- });
424
- } catch (error) {
425
- if (isENOENTError(error)) {
426
- // replicate the behavior of `rm -rf` and ignore ENOENT
427
- return;
428
- }
429
- if (isENOTDIRError(error) || isEEXISTError(error)) {
430
- // the backup path already exists and ends in a file
431
- // rather than a directory, so we have to remove the file
432
- // before we can move the current path to the backup path
433
- let dir = path.dirname(backupPath);
434
- while(dir !== this.syncJson.directory.absolute(".gadget/backup")){
435
- const stats = await fs.stat(dir);
436
- // eslint-disable-next-line max-depth
437
- if (!stats.isDirectory()) {
438
- // this file is in the way, so remove it
439
- ctx.log.debug("removing file in the way of backup path", {
440
- currentPath,
441
- backupPath,
442
- file: dir
443
- });
444
- await fs.remove(dir);
445
- }
446
- dir = path.dirname(dir);
447
- }
448
- // still throw the error so we retry
449
- }
450
- throw error;
451
- }
452
- }, {
453
- // windows tends to run into these issues way more often than
454
- // mac/linux, so we retry more times
455
- retries: config.windows ? 4 : 2,
456
- minTimeout: ms("100ms"),
457
- onFailedAttempt: (error)=>{
458
- ctx.log.warn("failed to move file to backup", {
459
- error,
460
- currentPath,
461
- backupPath
462
- });
463
- }
464
- });
465
- });
466
- for (const directoryWithDeletedFile of Array.from(directoriesWithDeletedFiles.values()).sort().reverse()){
467
- if (options.files.some((file)=>file.path === directoryWithDeletedFile)) {
468
- continue;
469
- }
470
- try {
471
- // delete any empty directories that contained a deleted file.
472
- // if the empty directory should continue to exist, we would
473
- // have received an event to create it above
474
- await fs.rmdir(this.syncJson.directory.absolute(directoryWithDeletedFile));
475
- changes.set(directoryWithDeletedFile, {
476
- type: "delete"
477
- });
478
- } catch (error) {
479
- if (isENOENTError(error) || isENOTEMPTYError(error)) {
480
- continue;
481
- }
482
- throw error;
483
- }
484
- }
485
- await pMap(options.files, async (file)=>{
486
- const absolutePath = this.syncJson.directory.absolute(file.path);
487
- if (await fs.pathExists(absolutePath)) {
488
- if (!file.path.endsWith("/")) {
489
- // only track file updates, not directory updates
490
- changes.set(file.path, {
491
- type: "update"
492
- });
493
- }
494
- } else {
495
- changes.set(file.path, {
496
- type: "create"
497
- });
498
- }
499
- if (file.path.endsWith("/")) {
500
- await fs.ensureDir(absolutePath);
501
- } else {
502
- await fs.outputFile(absolutePath, Buffer.from(file.content, file.encoding));
503
- }
504
- if (supportsPermissions) {
505
- // the os's default umask makes setting the mode during creation
506
- // not work, so an additional fs.chmod call is necessary to
507
- // ensure the file has the correct mode
508
- await fs.chmod(absolutePath, file.mode & 0o777);
509
- }
510
- if (absolutePath === this.syncJson.directory.absolute(".ignore")) {
511
- await this.syncJson.directory.loadIgnoreFile();
512
- }
513
- });
514
- await this.syncJson.save(String(filesVersion));
515
- options.spinner?.clear();
516
- printChanges(ctx, {
517
- changes,
518
- tense: "past",
519
- title: sprint`{greenBright ${symbol.arrowDown}} Pulled ${pluralize("file", changes.size)}. ${ts()}`,
520
- ...options.printEnvironmentChangesOptions,
521
- includeDotGadget: !!ctx.args["--verbose"]
522
- });
523
- if (changes.has("yarn.lock")) {
524
- const spinner = spin({
525
- ensureEmptyLineAbove: true
526
- })('Running "yarn install --check-files"');
527
- try {
528
- await execa("yarn", [
529
- "install",
530
- "--check-files"
531
- ], {
532
- cwd: this.syncJson.directory.path
533
- });
534
- spinner.succeed`Ran "yarn install --check-files" ${ts()}`;
535
- } catch (error) {
536
- spinner.fail();
537
- ctx.log.error("yarn install failed", {
538
- error
539
- });
540
- const message = serializeError(error).message;
541
- if (message) {
542
- println({
543
- ensureEmptyLineAbove: true,
544
- indent: 2
545
- })(message);
546
- }
547
- }
548
- }
549
- return changes;
550
- }
551
- async _merge(ctx, { hashes: { localChanges, environmentChanges, environmentFilesVersion }, printLocalChangesOptions, printEnvironmentChangesOptions }) {
552
- const conflicts = getConflicts({
553
- localChanges,
554
- environmentChanges
555
- });
556
- if (conflicts.size > 0) {
557
- ctx.log.debug("conflicts detected", {
558
- conflicts
559
- });
560
- let preference = ctx.args["--prefer"];
561
- if (!preference) {
562
- printConflicts({
563
- conflicts
564
- });
565
- preference = await select({
566
- choices: Object.values(MergeConflictPreference)
567
- })`
568
- {bold How should we resolve these conflicts?}
569
- `;
570
- }
571
- switch(preference){
572
- case MergeConflictPreference.CANCEL:
573
- {
574
- process.exit(0);
575
- break;
576
- }
577
- case MergeConflictPreference.LOCAL:
578
- {
579
- environmentChanges = withoutConflictingChanges({
580
- conflicts,
581
- changes: environmentChanges
582
- });
583
- break;
584
- }
585
- case MergeConflictPreference.ENVIRONMENT:
586
- {
587
- localChanges = withoutConflictingChanges({
588
- conflicts,
589
- changes: localChanges
590
- });
591
- break;
592
- }
593
- }
594
- }
595
- if (environmentChanges.size > 0) {
596
- await this._getChangesFromEnvironment(ctx, {
597
- changes: environmentChanges,
598
- filesVersion: environmentFilesVersion,
599
- printEnvironmentChangesOptions
600
- });
601
- }
602
- if (localChanges.size > 0) {
603
- await this._sendChangesToEnvironment(ctx, {
604
- changes: localChanges,
605
- expectedFilesVersion: environmentFilesVersion,
606
- printLocalChangesOptions
607
- });
608
- }
609
- }
610
- async _getChangesFromEnvironment(ctx, { filesVersion, changes, printEnvironmentChangesOptions }) {
611
- ctx.log.debug("getting changes from gadget", {
612
- filesVersion,
613
- changes
614
- });
615
- const created = changes.created();
616
- const updated = changes.updated();
617
- const spinner = spin({
618
- ensureEmptyLineAbove: true
619
- })(sprintChanges(ctx, {
620
- changes,
621
- tense: "present",
622
- title: sprint`Pulling ${pluralize("file", changes.size)}.`,
623
- ...printEnvironmentChangesOptions
624
- }));
625
- try {
626
- let files = [];
627
- if (created.length > 0 || updated.length > 0) {
628
- const { fileSyncFiles } = await this.syncJson.edit.query({
629
- query: FILE_SYNC_FILES_QUERY,
630
- variables: {
631
- paths: [
632
- ...created,
633
- ...updated
634
- ],
635
- filesVersion: String(filesVersion),
636
- encoding: FileSyncEncoding.Base64
637
- }
638
- });
639
- files = fileSyncFiles.files;
640
- }
641
- await this.writeToLocalFilesystem(ctx, {
642
- filesVersion,
643
- files,
644
- delete: changes.deleted(),
645
- spinner,
646
- printEnvironmentChangesOptions
647
- });
648
- } catch (error) {
649
- spinner.fail();
650
- throw error;
651
- }
652
- }
653
- async _sendChangesToEnvironment(ctx, { changes, expectedFilesVersion = this.syncJson.filesVersion, printLocalChangesOptions }) {
654
- ctx.log.debug("sending changes to gadget", {
655
- expectedFilesVersion,
656
- changes
657
- });
658
- const changed = [];
659
- const deleted = [];
660
- await pMap(changes, async ([normalizedPath, change])=>{
661
- if (change.type === "delete") {
662
- deleted.push({
663
- path: normalizedPath
664
- });
665
- return;
666
- }
667
- const absolutePath = this.syncJson.directory.absolute(normalizedPath);
668
- let stats;
669
- try {
670
- stats = await fs.stat(absolutePath);
671
- } catch (error) {
672
- swallowEnoent(error);
673
- ctx.log.debug("skipping change because file doesn't exist", {
674
- path: normalizedPath
675
- });
676
- return;
677
- }
678
- let content = "";
679
- if (stats.isFile()) {
680
- content = await fs.readFile(absolutePath, FileSyncEncoding.Base64);
681
- }
682
- let oldPath;
683
- if (change.type === "create" && change.oldPath) {
684
- oldPath = change.oldPath;
685
- }
686
- changed.push({
687
- content,
688
- oldPath,
689
- path: normalizedPath,
690
- mode: stats.mode,
691
- encoding: FileSyncEncoding.Base64
692
- });
693
- });
694
- if (changed.length === 0 && deleted.length === 0) {
695
- ctx.log.debug("skipping send because there are no changes");
696
- return;
697
- }
698
- const contentLength = changed.map((change)=>change.content.length).reduce((a, b)=>a + b, 0);
699
- if (contentLength > MAX_PUSH_CONTENT_LENGTH) {
700
- throw new EdgeCaseError(sprint`
701
- {underline Your file changes are too large to push.}
702
-
703
- Run "ggt status" to see your changes and consider
704
- ignoring some files or pushing in smaller batches.
705
- `);
706
- }
707
- const spinner = spin({
708
- ensureEmptyLineAbove: true
709
- })(sprintChanges(ctx, {
710
- changes,
711
- tense: "present",
712
- title: sprintln`Pushing ${pluralize("file", changed.length + deleted.length)}. ${symbol.arrowRight}`,
713
- ...printLocalChangesOptions
714
- }));
715
- try {
716
- const { publishFileSyncEvents: { remoteFilesVersion, problems: filesyncProblems } } = await this.syncJson.edit.mutate({
717
- mutation: PUBLISH_FILE_SYNC_EVENTS_MUTATION,
718
- variables: {
719
- input: {
720
- expectedRemoteFilesVersion: String(expectedFilesVersion),
721
- changed,
722
- deleted
723
- }
724
- },
725
- http: {
726
- retry: {
727
- // we can retry this request because
728
- // expectedRemoteFilesVersion makes it idempotent
729
- methods: [
730
- "POST"
731
- ],
732
- calculateDelay: ({ error, computedValue })=>{
733
- if (isFilesVersionMismatchError(error.response?.body)) {
734
- // don't retry if we get a files version mismatch error
735
- return 0;
736
- }
737
- return computedValue;
738
- }
739
- }
740
- }
741
- });
742
- if (BigInt(remoteFilesVersion) > expectedFilesVersion + 1n) {
743
- // we can't save the remoteFilesVersion because we haven't
744
- // received the intermediate filesVersions yet
745
- throw new Error("Files version mismatch");
746
- }
747
- await this.syncJson.save(remoteFilesVersion);
748
- spinner.succeed(sprintChanges(ctx, {
749
- changes,
750
- tense: "past",
751
- title: sprintln`Pushed ${pluralize("file", changed.length + deleted.length)}. ${symbol.arrowRight} ${ts()}`,
752
- ...printLocalChangesOptions
753
- }));
754
- if (filesyncProblems.length > 0) {
755
- println({
756
- ensureEmptyLineAbove: true
757
- })`
758
- {red Gadget has detected the following fatal errors with your files:}
759
-
760
- ${sprintProblems({
761
- problems: filesyncProblemsToProblems(filesyncProblems),
762
- showFileTypes: false,
763
- indent: 10
764
- })}
765
-
766
- {red Your app will not be operational until all fatal errors are fixed.}
767
- `;
768
- }
769
- } catch (error) {
770
- if (isFilesVersionMismatchError(error)) {
771
- spinner.clear();
772
- } else {
773
- spinner.fail();
774
- }
775
- throw error;
776
- }
777
- }
778
- constructor(syncJson){
779
- _define_property(this, "syncJson", void 0);
780
- /**
781
- * A FIFO async callback queue that ensures we process filesync events
782
- * in the order we receive them.
783
- */ _define_property(this, "_syncOperations", void 0);
784
- this.syncJson = syncJson;
785
- this._syncOperations = new PQueue({
786
- concurrency: 1
787
- });
788
- }
789
- }
790
-
791
- //# sourceMappingURL=filesync.js.map