@gadgetinc/ggt 0.3.2 → 0.4.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 (136) hide show
  1. package/README.md +139 -76
  2. package/bin/dev.js +4 -7
  3. package/lib/__generated__/graphql.js.map +1 -1
  4. package/lib/commands/deploy.js +227 -0
  5. package/lib/commands/deploy.js.map +1 -0
  6. package/lib/commands/list.js +28 -21
  7. package/lib/commands/list.js.map +1 -1
  8. package/lib/commands/login.js +22 -20
  9. package/lib/commands/login.js.map +1 -1
  10. package/lib/commands/logout.js +13 -9
  11. package/lib/commands/logout.js.map +1 -1
  12. package/lib/commands/root.js +89 -56
  13. package/lib/commands/root.js.map +1 -1
  14. package/lib/commands/sync.js +256 -499
  15. package/lib/commands/sync.js.map +1 -1
  16. package/lib/commands/version.js +21 -0
  17. package/lib/commands/version.js.map +1 -0
  18. package/lib/commands/whoami.js +15 -11
  19. package/lib/commands/whoami.js.map +1 -1
  20. package/lib/main.js +4 -10
  21. package/lib/main.js.map +1 -1
  22. package/lib/services/{app.js → app/app.js} +9 -5
  23. package/lib/services/app/app.js.map +1 -0
  24. package/lib/services/app/arg.js +28 -0
  25. package/lib/services/app/arg.js.map +1 -0
  26. package/lib/services/app/edit-graphql.js +389 -0
  27. package/lib/services/app/edit-graphql.js.map +1 -0
  28. package/lib/services/command/arg.js +53 -0
  29. package/lib/services/command/arg.js.map +1 -0
  30. package/lib/services/command/command.js +27 -0
  31. package/lib/services/command/command.js.map +1 -0
  32. package/lib/services/command/context.js +60 -0
  33. package/lib/services/command/context.js.map +1 -0
  34. package/lib/services/{config.js → config/config.js} +32 -35
  35. package/lib/services/config/config.js.map +1 -0
  36. package/lib/services/config/env.js +22 -0
  37. package/lib/services/config/env.js.map +1 -0
  38. package/lib/services/config/package-json.js +9 -0
  39. package/lib/services/config/package-json.js.map +1 -0
  40. package/lib/services/filesync/changes.js +97 -0
  41. package/lib/services/filesync/changes.js.map +1 -0
  42. package/lib/services/filesync/conflicts.js +137 -0
  43. package/lib/services/filesync/conflicts.js.map +1 -0
  44. package/lib/services/filesync/directory.js +253 -0
  45. package/lib/services/filesync/directory.js.map +1 -0
  46. package/lib/services/filesync/error.js +67 -0
  47. package/lib/services/filesync/error.js.map +1 -0
  48. package/lib/services/filesync/file.js +3 -0
  49. package/lib/services/filesync/file.js.map +1 -0
  50. package/lib/services/filesync/filesync.js +675 -0
  51. package/lib/services/filesync/filesync.js.map +1 -0
  52. package/lib/services/filesync/hashes.js +150 -0
  53. package/lib/services/filesync/hashes.js.map +1 -0
  54. package/lib/services/http/auth.js +41 -0
  55. package/lib/services/http/auth.js.map +1 -0
  56. package/lib/services/http/http.js +64 -0
  57. package/lib/services/http/http.js.map +1 -0
  58. package/lib/services/output/log/field.js +3 -0
  59. package/lib/services/output/log/field.js.map +1 -0
  60. package/lib/services/output/log/format/format.js +8 -0
  61. package/lib/services/output/log/format/format.js.map +1 -0
  62. package/lib/services/output/log/format/json.js +45 -0
  63. package/lib/services/output/log/format/json.js.map +1 -0
  64. package/lib/services/output/log/format/pretty.js +147 -0
  65. package/lib/services/output/log/format/pretty.js.map +1 -0
  66. package/lib/services/output/log/level.js +41 -0
  67. package/lib/services/output/log/level.js.map +1 -0
  68. package/lib/services/output/log/logger.js +40 -0
  69. package/lib/services/output/log/logger.js.map +1 -0
  70. package/lib/services/output/log/printer.js +120 -0
  71. package/lib/services/output/log/printer.js.map +1 -0
  72. package/lib/services/output/log/structured.js +52 -0
  73. package/lib/services/output/log/structured.js.map +1 -0
  74. package/lib/services/{notify.js → output/notify.js} +7 -6
  75. package/lib/services/output/notify.js.map +1 -0
  76. package/lib/services/output/prompt.js +52 -0
  77. package/lib/services/output/prompt.js.map +1 -0
  78. package/lib/services/output/report.js +162 -0
  79. package/lib/services/output/report.js.map +1 -0
  80. package/lib/services/output/sprint.js +21 -0
  81. package/lib/services/output/sprint.js.map +1 -0
  82. package/lib/services/{output.js → output/stream.js} +18 -23
  83. package/lib/services/output/stream.js.map +1 -0
  84. package/lib/services/{version.js → output/update.js} +26 -18
  85. package/lib/services/output/update.js.map +1 -0
  86. package/lib/services/user/session.js +50 -0
  87. package/lib/services/user/session.js.map +1 -0
  88. package/lib/services/{user.js → user/user.js} +24 -17
  89. package/lib/services/user/user.js.map +1 -0
  90. package/lib/services/util/boolean.js +15 -0
  91. package/lib/services/util/boolean.js.map +1 -0
  92. package/lib/services/util/collection.js +38 -0
  93. package/lib/services/util/collection.js.map +1 -0
  94. package/lib/services/util/function.js +97 -0
  95. package/lib/services/util/function.js.map +1 -0
  96. package/lib/services/util/is.js +46 -0
  97. package/lib/services/util/is.js.map +1 -0
  98. package/lib/services/util/number.js +27 -0
  99. package/lib/services/util/number.js.map +1 -0
  100. package/lib/services/util/object.js +101 -0
  101. package/lib/services/util/object.js.map +1 -0
  102. package/lib/services/util/paths.js +36 -0
  103. package/lib/services/util/paths.js.map +1 -0
  104. package/lib/services/{promise.js → util/promise.js} +4 -4
  105. package/lib/services/util/promise.js.map +1 -0
  106. package/npm-shrinkwrap.json +2416 -1547
  107. package/package.json +52 -46
  108. package/lib/commands/index.js +0 -9
  109. package/lib/commands/index.js.map +0 -1
  110. package/lib/services/app.js.map +0 -1
  111. package/lib/services/args.js +0 -28
  112. package/lib/services/args.js.map +0 -1
  113. package/lib/services/config.js.map +0 -1
  114. package/lib/services/edit-graphql.js +0 -193
  115. package/lib/services/edit-graphql.js.map +0 -1
  116. package/lib/services/errors.js +0 -274
  117. package/lib/services/errors.js.map +0 -1
  118. package/lib/services/filesync.js +0 -404
  119. package/lib/services/filesync.js.map +0 -1
  120. package/lib/services/fs-utils.js +0 -33
  121. package/lib/services/fs-utils.js.map +0 -1
  122. package/lib/services/http.js +0 -53
  123. package/lib/services/http.js.map +0 -1
  124. package/lib/services/log.js +0 -45
  125. package/lib/services/log.js.map +0 -1
  126. package/lib/services/notify.js.map +0 -1
  127. package/lib/services/output.js.map +0 -1
  128. package/lib/services/promise.js.map +0 -1
  129. package/lib/services/session.js +0 -27
  130. package/lib/services/session.js.map +0 -1
  131. package/lib/services/sleep.js +0 -19
  132. package/lib/services/sleep.js.map +0 -1
  133. package/lib/services/timeout.js +0 -8
  134. package/lib/services/timeout.js.map +0 -1
  135. package/lib/services/user.js.map +0 -1
  136. package/lib/services/version.js.map +0 -1
@@ -0,0 +1,253 @@
1
+ /**
2
+ * DO NOT MODIFY
3
+ *
4
+ * Everything in this file also exists in ggt to ensure that this logic
5
+ * is the same between the two projects.
6
+ */ import { _ as _define_property } from "@swc/helpers/_/_define_property";
7
+ import fs from "fs-extra";
8
+ import ignore from "ignore";
9
+ import assert from "node:assert";
10
+ import { createHash } from "node:crypto";
11
+ import path from "node:path";
12
+ import { Transform } from "node:stream";
13
+ import { pipeline } from "node:stream/promises";
14
+ import normalizePath from "normalize-path";
15
+ /**
16
+ * Paths that are always ignored, regardless of the contents of the `.ignore` file.
17
+ */ export const ALWAYS_IGNORE_PATHS = [
18
+ ".DS_Store",
19
+ "node_modules",
20
+ ".git"
21
+ ];
22
+ /**
23
+ * Paths that are ignored when hashing the directory.
24
+ *
25
+ * NOTE: This is the _only_ thing that is allowed to be different between gadget and ggt.
26
+ */ export const HASHING_IGNORE_PATHS = [
27
+ ".gadget/sync.json",
28
+ ".gadget/backup"
29
+ ];
30
+ /**
31
+ * Represents a directory that is being synced.
32
+ */ export class Directory {
33
+ /**
34
+ * Initializes a directory to be synced.
35
+ *
36
+ * If the directory does not exist, it is created.
37
+ *
38
+ * @param dir - The directory to initialize.
39
+ * @returns A Promise that resolves to a Directory instance.
40
+ */ static async init(dir) {
41
+ const directory = new Directory(dir);
42
+ await directory.loadIgnoreFile();
43
+ return directory;
44
+ }
45
+ /**
46
+ * Returns the relative path from this directory to the specified path.
47
+ *
48
+ * @param to - The path to which the relative path is calculated.
49
+ * @returns The relative path from this directory to the specified path.
50
+ */ relative(to) {
51
+ if (!path.isAbsolute(to)) {
52
+ // the filepath is already relative
53
+ return to;
54
+ }
55
+ return path.relative(this.path, to);
56
+ }
57
+ /**
58
+ * Returns the absolute path by resolving the given path segments
59
+ * relative to the directory path.
60
+ *
61
+ * @param pathSegments The path segments to resolve.
62
+ * @returns The absolute path.
63
+ */ absolute(...pathSegments) {
64
+ const result = path.resolve(this.path, ...pathSegments);
65
+ assert(result.startsWith(this.path), `expected ${result} to be within ${this.path}`);
66
+ return result;
67
+ }
68
+ /**
69
+ * Similar to {@linkcode relative} in that it converts an absolute
70
+ * path into a relative one from {@linkcode path}. However, it also
71
+ * changes any slashes to be posix/unix-like forward slashes,
72
+ * condenses repeated slashes into a single slash, and adds a trailing
73
+ * slash if the path is a directory.
74
+ *
75
+ * This is used when sending files to Gadget to ensure that the paths
76
+ * are consistent across platforms.
77
+ *
78
+ * @see https://www.npmjs.com/package/normalize-path
79
+ */ normalize(filepath, isDirectory) {
80
+ if (path.isAbsolute(filepath)) {
81
+ filepath = this.relative(filepath);
82
+ }
83
+ // true = trim trailing slashes
84
+ filepath = normalizePath(filepath, true);
85
+ if (isDirectory) {
86
+ filepath += "/";
87
+ }
88
+ return filepath;
89
+ }
90
+ /**
91
+ * Loads the `.ignore` file in the directory. If the file does not
92
+ * exist, it is silently ignored.
93
+ */ async loadIgnoreFile() {
94
+ this._ignorer = ignore.default();
95
+ this._ignorer.add(ALWAYS_IGNORE_PATHS);
96
+ try {
97
+ const content = await fs.readFile(this.absolute(".ignore"), "utf8");
98
+ this._ignorer.add(content);
99
+ } catch (error) {
100
+ swallowEnoent(error);
101
+ }
102
+ }
103
+ /**
104
+ * Determines if a file should be ignored based on its filepath.
105
+ *
106
+ * @param filepath - The filepath of the file to check.
107
+ * @returns True if the file should be ignored, false otherwise.
108
+ */ ignores(filepath) {
109
+ filepath = this.relative(filepath);
110
+ if (filepath === "") {
111
+ // don't ignore the root dir
112
+ return false;
113
+ }
114
+ if (filepath.startsWith("..")) {
115
+ // anything above the root dir is ignored
116
+ return true;
117
+ }
118
+ // false = don't trim trailing slashes
119
+ filepath = normalizePath(filepath, false);
120
+ if (this._isHashing && HASHING_IGNORE_PATHS.some((ignored)=>filepath.startsWith(ignored))) {
121
+ // special case for hashing
122
+ return true;
123
+ }
124
+ return this._ignorer.ignores(filepath);
125
+ }
126
+ /**
127
+ * Recursively walks through the directory and yields all non-ignored
128
+ * files and directories within it.
129
+ *
130
+ * @yields - The normalized path of each file and directory.
131
+ */ async *walk({ dir = this.path } = {}) {
132
+ // don't yield the root directory
133
+ if (dir !== this.path) {
134
+ yield this.normalize(dir, true);
135
+ }
136
+ for await (const entry of (await fs.opendir(dir))){
137
+ const filepath = path.join(dir, entry.name);
138
+ if (this.ignores(filepath)) {
139
+ continue;
140
+ }
141
+ if (entry.isDirectory()) {
142
+ yield* this.walk({
143
+ dir: filepath
144
+ });
145
+ } else if (entry.isFile()) {
146
+ yield this.normalize(filepath, false);
147
+ }
148
+ }
149
+ }
150
+ /**
151
+ * Calculates the hash of each file and directory and returns an
152
+ * object containing the hashes keyed by the normalized file path.
153
+ *
154
+ * @returns A Promise that resolves to an object containing the hashes
155
+ * of each file.
156
+ */ async hashes() {
157
+ try {
158
+ this._isHashing = true;
159
+ const files = {};
160
+ for await (const normalizedPath of this.walk()){
161
+ const absolutePath = this.absolute(normalizedPath);
162
+ files[normalizedPath] = await hash(absolutePath);
163
+ }
164
+ return files;
165
+ } finally{
166
+ this._isHashing = false;
167
+ }
168
+ }
169
+ constructor(/**
170
+ * An absolute path to the directory that is being synced.
171
+ */ path){
172
+ _define_property(this, "path", void 0);
173
+ /**
174
+ * A gitignore-style file parser used to determine which files to
175
+ * ignore while syncing.
176
+ *
177
+ * @see https://www.npmjs.com/package/ignore
178
+ */ _define_property(this, "_ignorer", void 0);
179
+ /**
180
+ * Whether the directory is currently being hashed.
181
+ */ _define_property(this, "_isHashing", void 0);
182
+ this.path = path;
183
+ this._isHashing = false;
184
+ }
185
+ }
186
+ /**
187
+ * Whether the current platform supports Unix-style file permissions.
188
+ *
189
+ * Windows doesn't support Unix-style file permissions and all file
190
+ * permissions retrieved via `node:fs` on Windows are translated to 666
191
+ * or 444.
192
+ */ export const supportsPermissions = process.platform === "linux" || process.platform === "darwin";
193
+ /**
194
+ * Calculates the {@linkcode Hash} of the file or directory at the
195
+ * specified absolute path.
196
+ *
197
+ * @param absolutePath The absolute path to the file or directory.
198
+ * @returns A Promise that resolves to the {@linkcode Hash} of the file
199
+ * or directory.
200
+ */ const hash = async (absolutePath)=>{
201
+ const sha1 = createHash("sha1");
202
+ sha1.update(path.basename(absolutePath));
203
+ const stats = await fs.stat(absolutePath);
204
+ let permissions;
205
+ if (supportsPermissions) {
206
+ // strip everything but the permissions
207
+ permissions = stats.mode & 0o777;
208
+ }
209
+ if (stats.isDirectory()) {
210
+ return {
211
+ sha1: sha1.digest("hex"),
212
+ permissions
213
+ };
214
+ }
215
+ // windows uses CRLF line endings whereas unix uses LF line endings so
216
+ // we always strip out CR bytes (0x0d) when hashing files. this does
217
+ // make us blind to files that only differ by CR bytes, but that's a
218
+ // tradeoff we're willing to make.
219
+ const removeCR = new Transform({
220
+ transform (chunk, _encoding, callback) {
221
+ if (!chunk.includes(0x0d)) {
222
+ callback(undefined, chunk);
223
+ return;
224
+ }
225
+ const filteredChunk = Buffer.alloc(chunk.length);
226
+ let i = 0;
227
+ for (const byte of chunk){
228
+ if (byte !== 0x0d) {
229
+ filteredChunk[i++] = byte;
230
+ }
231
+ }
232
+ callback(undefined, filteredChunk.slice(0, i));
233
+ }
234
+ });
235
+ await pipeline(fs.createReadStream(absolutePath), removeCR, sha1);
236
+ return {
237
+ sha1: sha1.digest("hex"),
238
+ permissions
239
+ };
240
+ };
241
+ /**
242
+ * Swallows ENOENT errors and throws any other errors.
243
+ *
244
+ * @param error - The error to handle.
245
+ * @throws The original error if it is not an ENOENT error.
246
+ */ export const swallowEnoent = (error)=>{
247
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
248
+ return;
249
+ }
250
+ throw error;
251
+ };
252
+
253
+ //# sourceMappingURL=directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/services/filesync/directory.ts"],"sourcesContent":["/**\n * DO NOT MODIFY\n *\n * Everything in this file also exists in ggt to ensure that this logic\n * is the same between the two projects.\n */\nimport fs from \"fs-extra\";\nimport type { Ignore } from \"ignore\";\nimport ignore from \"ignore\";\nimport assert from \"node:assert\";\nimport { createHash } from \"node:crypto\";\nimport path from \"node:path\";\nimport { Transform } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport normalizePath from \"normalize-path\";\n\n/**\n * Paths that are always ignored, regardless of the contents of the `.ignore` file.\n */\nexport const ALWAYS_IGNORE_PATHS = [\".DS_Store\", \"node_modules\", \".git\"] as const;\n\n/**\n * Paths that are ignored when hashing the directory.\n *\n * NOTE: This is the _only_ thing that is allowed to be different between gadget and ggt.\n */\nexport const HASHING_IGNORE_PATHS = [\".gadget/sync.json\", \".gadget/backup\"] as const;\n\n/**\n * Represents a directory that is being synced.\n */\nexport class Directory {\n /**\n * A gitignore-style file parser used to determine which files to\n * ignore while syncing.\n *\n * @see https://www.npmjs.com/package/ignore\n */\n private _ignorer!: Ignore;\n\n /**\n * Whether the directory is currently being hashed.\n */\n private _isHashing = false;\n\n private constructor(\n /**\n * An absolute path to the directory that is being synced.\n */\n readonly path: string,\n ) {}\n\n /**\n * Initializes a directory to be synced.\n *\n * If the directory does not exist, it is created.\n *\n * @param dir - The directory to initialize.\n * @returns A Promise that resolves to a Directory instance.\n */\n static async init(dir: string): Promise<Directory> {\n const directory = new Directory(dir);\n await directory.loadIgnoreFile();\n return directory;\n }\n\n /**\n * Returns the relative path from this directory to the specified path.\n *\n * @param to - The path to which the relative path is calculated.\n * @returns The relative path from this directory to the specified path.\n */\n relative(to: string): string {\n if (!path.isAbsolute(to)) {\n // the filepath is already relative\n return to;\n }\n\n return path.relative(this.path, to);\n }\n\n /**\n * Returns the absolute path by resolving the given path segments\n * relative to the directory path.\n *\n * @param pathSegments The path segments to resolve.\n * @returns The absolute path.\n */\n absolute(...pathSegments: string[]): string {\n const result = path.resolve(this.path, ...pathSegments);\n assert(result.startsWith(this.path), `expected ${result} to be within ${this.path}`);\n return result;\n }\n\n /**\n * Similar to {@linkcode relative} in that it converts an absolute\n * path into a relative one from {@linkcode path}. However, it also\n * changes any slashes to be posix/unix-like forward slashes,\n * condenses repeated slashes into a single slash, and adds a trailing\n * slash if the path is a directory.\n *\n * This is used when sending files to Gadget to ensure that the paths\n * are consistent across platforms.\n *\n * @see https://www.npmjs.com/package/normalize-path\n */\n normalize(filepath: string, isDirectory: boolean): string {\n if (path.isAbsolute(filepath)) {\n filepath = this.relative(filepath);\n }\n\n // true = trim trailing slashes\n filepath = normalizePath(filepath, true);\n\n if (isDirectory) {\n filepath += \"/\";\n }\n\n return filepath;\n }\n\n /**\n * Loads the `.ignore` file in the directory. If the file does not\n * exist, it is silently ignored.\n */\n async loadIgnoreFile(): Promise<void> {\n this._ignorer = ignore.default();\n this._ignorer.add(ALWAYS_IGNORE_PATHS);\n\n try {\n const content = await fs.readFile(this.absolute(\".ignore\"), \"utf8\");\n this._ignorer.add(content);\n } catch (error) {\n swallowEnoent(error);\n }\n }\n\n /**\n * Determines if a file should be ignored based on its filepath.\n *\n * @param filepath - The filepath of the file to check.\n * @returns True if the file should be ignored, false otherwise.\n */\n ignores(filepath: string): boolean {\n filepath = this.relative(filepath);\n if (filepath === \"\") {\n // don't ignore the root dir\n return false;\n }\n\n if (filepath.startsWith(\"..\")) {\n // anything above the root dir is ignored\n return true;\n }\n\n // false = don't trim trailing slashes\n filepath = normalizePath(filepath, false);\n if (this._isHashing && HASHING_IGNORE_PATHS.some((ignored) => filepath.startsWith(ignored))) {\n // special case for hashing\n return true;\n }\n\n return this._ignorer.ignores(filepath);\n }\n\n /**\n * Recursively walks through the directory and yields all non-ignored\n * files and directories within it.\n *\n * @yields - The normalized path of each file and directory.\n */\n async *walk({ dir = this.path } = {}): AsyncGenerator<string> {\n // don't yield the root directory\n if (dir !== this.path) {\n yield this.normalize(dir, true);\n }\n\n for await (const entry of await fs.opendir(dir)) {\n const filepath = path.join(dir, entry.name);\n if (this.ignores(filepath)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n yield* this.walk({ dir: filepath });\n } else if (entry.isFile()) {\n yield this.normalize(filepath, false);\n }\n }\n }\n\n /**\n * Calculates the hash of each file and directory and returns an\n * object containing the hashes keyed by the normalized file path.\n *\n * @returns A Promise that resolves to an object containing the hashes\n * of each file.\n */\n async hashes(): Promise<Hashes> {\n try {\n this._isHashing = true;\n const files = {} as Hashes;\n\n for await (const normalizedPath of this.walk()) {\n const absolutePath = this.absolute(normalizedPath);\n files[normalizedPath] = await hash(absolutePath);\n }\n\n return files;\n } finally {\n this._isHashing = false;\n }\n }\n}\n\n/**\n * Key/value pairs where the key is the normalized path and the value is\n * the result of {@linkcode hash} for that path.\n */\nexport type Hashes = Record<string, Hash>;\n\nexport type Hash = {\n /**\n * The SHA-1 hash of the file or directory.\n *\n * If the path points to a directory, the hash is calculated based on\n * the directory's basename. If the path points to a file, the hash is\n * calculated based on the file's basename and contents.\n */\n sha1: string;\n\n /**\n * The Unix-style file permissions of the file or directory, or\n * undefined if the platform that generated this hash doesn't support\n * them.\n *\n * @example 0o644\n * @see supportsPermissions\n */\n permissions?: number;\n};\n\n/**\n * Whether the current platform supports Unix-style file permissions.\n *\n * Windows doesn't support Unix-style file permissions and all file\n * permissions retrieved via `node:fs` on Windows are translated to 666\n * or 444.\n */\nexport const supportsPermissions = process.platform === \"linux\" || process.platform === \"darwin\";\n\n/**\n * Calculates the {@linkcode Hash} of the file or directory at the\n * specified absolute path.\n *\n * @param absolutePath The absolute path to the file or directory.\n * @returns A Promise that resolves to the {@linkcode Hash} of the file\n * or directory.\n */\nconst hash = async (absolutePath: string): Promise<Hash> => {\n const sha1 = createHash(\"sha1\");\n sha1.update(path.basename(absolutePath));\n\n const stats = await fs.stat(absolutePath);\n\n let permissions;\n if (supportsPermissions) {\n // strip everything but the permissions\n permissions = stats.mode & 0o777;\n }\n\n if (stats.isDirectory()) {\n return { sha1: sha1.digest(\"hex\"), permissions };\n }\n\n // windows uses CRLF line endings whereas unix uses LF line endings so\n // we always strip out CR bytes (0x0d) when hashing files. this does\n // make us blind to files that only differ by CR bytes, but that's a\n // tradeoff we're willing to make.\n const removeCR = new Transform({\n transform(chunk: Buffer, _encoding, callback) {\n if (!chunk.includes(0x0d)) {\n callback(undefined, chunk);\n return;\n }\n\n const filteredChunk = Buffer.alloc(chunk.length);\n let i = 0;\n for (const byte of chunk) {\n if (byte !== 0x0d) {\n filteredChunk[i++] = byte;\n }\n }\n\n callback(undefined, filteredChunk.slice(0, i));\n },\n });\n\n await pipeline(fs.createReadStream(absolutePath), removeCR, sha1);\n\n return { sha1: sha1.digest(\"hex\"), permissions };\n};\n\n/**\n * Swallows ENOENT errors and throws any other errors.\n *\n * @param error - The error to handle.\n * @throws The original error if it is not an ENOENT error.\n */\nexport const swallowEnoent = (error: unknown): void => {\n if (error && typeof error === \"object\" && \"code\" in error && error.code === \"ENOENT\") {\n return;\n }\n throw error;\n};\n"],"names":["fs","ignore","assert","createHash","path","Transform","pipeline","normalizePath","ALWAYS_IGNORE_PATHS","HASHING_IGNORE_PATHS","Directory","init","dir","directory","loadIgnoreFile","relative","to","isAbsolute","absolute","pathSegments","result","resolve","startsWith","normalize","filepath","isDirectory","_ignorer","default","add","content","readFile","error","swallowEnoent","ignores","_isHashing","some","ignored","walk","entry","opendir","join","name","isFile","hashes","files","normalizedPath","absolutePath","hash","supportsPermissions","process","platform","sha1","update","basename","stats","stat","permissions","mode","digest","removeCR","transform","chunk","_encoding","callback","includes","undefined","filteredChunk","Buffer","alloc","length","i","byte","slice","createReadStream","code"],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
+ import { CLIError, IsBug } from "../output/report.js";
3
+ import { sprint } from "../output/sprint.js";
4
+ export class YarnNotFoundError extends CLIError {
5
+ render() {
6
+ return sprint`
7
+ Yarn must be installed to sync your application. You can install it by running:
8
+
9
+ $ npm install --global yarn
10
+
11
+ For more information, see: https://classic.yarnpkg.com/en/docs/install
12
+ `;
13
+ }
14
+ constructor(){
15
+ super("Yarn not found");
16
+ _define_property(this, "isBug", IsBug.NO);
17
+ }
18
+ }
19
+ export class InvalidSyncFileError extends CLIError {
20
+ render() {
21
+ return sprint`
22
+ We failed to find a ".gadget/sync.json" file in this directory:
23
+
24
+ ${this.dir}
25
+
26
+ If you're running 'ggt sync' for the first time, we recommend
27
+ using a gadget specific directory like this:
28
+
29
+ ggt sync ~/gadget/${this.app} --app ${this.app}
30
+
31
+ If you're certain you want to sync the contents of that directory
32
+ to Gadget, run 'ggt sync' again with the {bold --force} flag:
33
+
34
+ ggt sync ${this.dir} --app ${this.app} --force
35
+ `;
36
+ }
37
+ constructor(dir, app){
38
+ super("The .gadget/sync.json file was invalid or not found");
39
+ _define_property(this, "dir", void 0);
40
+ _define_property(this, "app", void 0);
41
+ _define_property(this, "isBug", void 0);
42
+ this.dir = dir;
43
+ this.app = app;
44
+ this.isBug = IsBug.NO;
45
+ this.app ??= "<name of app>";
46
+ }
47
+ }
48
+ export class TooManySyncAttemptsError extends CLIError {
49
+ render() {
50
+ return sprint`
51
+ We synced your local files with Gadget ${this.attempts} times, but
52
+ your local filesystem is still out of sync.
53
+
54
+ Make sure no one else is editing files in the Gadget editor
55
+ and try again.
56
+ `;
57
+ }
58
+ constructor(attempts){
59
+ super(`Failed to sync files after ${attempts} attempts.`);
60
+ _define_property(this, "attempts", void 0);
61
+ _define_property(this, "isBug", void 0);
62
+ this.attempts = attempts;
63
+ this.isBug = IsBug.MAYBE;
64
+ }
65
+ }
66
+
67
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/services/filesync/error.ts"],"sourcesContent":["import { CLIError, IsBug } from \"../output/report.js\";\nimport { sprint } from \"../output/sprint.js\";\n\nexport class YarnNotFoundError extends CLIError {\n isBug = IsBug.NO;\n\n constructor() {\n super(\"Yarn not found\");\n }\n\n protected render(): string {\n return sprint`\n Yarn must be installed to sync your application. You can install it by running:\n\n $ npm install --global yarn\n\n For more information, see: https://classic.yarnpkg.com/en/docs/install\n `;\n }\n}\n\nexport class InvalidSyncFileError extends CLIError {\n isBug = IsBug.NO;\n\n constructor(\n readonly dir: string,\n readonly app: string | undefined,\n ) {\n super(\"The .gadget/sync.json file was invalid or not found\");\n this.app ??= \"<name of app>\";\n }\n\n protected render(): string {\n return sprint`\n We failed to find a \".gadget/sync.json\" file in this directory:\n\n ${this.dir}\n\n If you're running 'ggt sync' for the first time, we recommend\n using a gadget specific directory like this:\n\n ggt sync ~/gadget/${this.app} --app ${this.app}\n\n If you're certain you want to sync the contents of that directory\n to Gadget, run 'ggt sync' again with the {bold --force} flag:\n\n ggt sync ${this.dir} --app ${this.app} --force\n `;\n }\n}\n\nexport class TooManySyncAttemptsError extends CLIError {\n isBug = IsBug.MAYBE;\n\n constructor(readonly attempts: number) {\n super(`Failed to sync files after ${attempts} attempts.`);\n }\n\n protected render(): string {\n return sprint`\n We synced your local files with Gadget ${this.attempts} times, but\n your local filesystem is still out of sync.\n\n Make sure no one else is editing files in the Gadget editor\n and try again.\n `;\n }\n}\n"],"names":["CLIError","IsBug","sprint","YarnNotFoundError","render","constructor","isBug","NO","InvalidSyncFileError","dir","app","TooManySyncAttemptsError","attempts","MAYBE"],"mappings":";AAAA,SAASA,QAAQ,EAAEC,KAAK,QAAQ,sBAAsB;AACtD,SAASC,MAAM,QAAQ,sBAAsB;AAE7C,OAAO,MAAMC,0BAA0BH;IAO3BI,SAAiB;QACzB,OAAOF,MAAM,CAAC;;;;;;IAMd,CAAC;IACH;IAZAG,aAAc;QACZ,KAAK,CAAC;QAHRC,uBAAAA,SAAQL,MAAMM,EAAE;IAIhB;AAWF;AAEA,OAAO,MAAMC,6BAA6BR;IAW9BI,SAAiB;QACzB,OAAOF,MAAM,CAAC;;;QAGV,EAAE,IAAI,CAACO,GAAG,CAAC;;;;;0BAKO,EAAE,IAAI,CAACC,GAAG,CAAC,OAAO,EAAE,IAAI,CAACA,GAAG,CAAC;;;;;iBAKtC,EAAE,IAAI,CAACD,GAAG,CAAC,OAAO,EAAE,IAAI,CAACC,GAAG,CAAC;IAC1C,CAAC;IACH;IAxBAL,YACE,AAASI,GAAW,EACpB,AAASC,GAAuB,CAChC;QACA,KAAK,CAAC;;;QANRJ,uBAAAA,SAAAA,KAAAA;aAGWG,MAAAA;aACAC,MAAAA;aAJXJ,QAAQL,MAAMM,EAAE;QAOd,IAAI,CAACG,GAAG,KAAK;IACf;AAmBF;AAEA,OAAO,MAAMC,iCAAiCX;IAOlCI,SAAiB;QACzB,OAAOF,MAAM,CAAC;+CAC6B,EAAE,IAAI,CAACU,QAAQ,CAAC;;;;;IAK3D,CAAC;IACH;IAZAP,YAAY,AAASO,QAAgB,CAAE;QACrC,KAAK,CAAC,CAAC,2BAA2B,EAAEA,SAAS,UAAU,CAAC;;QAH1DN,uBAAAA,SAAAA,KAAAA;aAEqBM,WAAAA;aAFrBN,QAAQL,MAAMY,KAAK;IAInB;AAWF"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/services/filesync/file.ts"],"sourcesContent":["import type { FileSyncEncoding } from \"../../__generated__/graphql.js\";\n\nexport type File = {\n path: string;\n oldPath?: string | null;\n mode: number;\n content: string;\n encoding: FileSyncEncoding;\n};\n"],"names":[],"mappings":"AAEA,WAME"}