@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.
- package/README.md +139 -76
- package/bin/dev.js +4 -7
- package/lib/__generated__/graphql.js.map +1 -1
- package/lib/commands/deploy.js +227 -0
- package/lib/commands/deploy.js.map +1 -0
- package/lib/commands/list.js +28 -21
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/login.js +22 -20
- package/lib/commands/login.js.map +1 -1
- package/lib/commands/logout.js +13 -9
- package/lib/commands/logout.js.map +1 -1
- package/lib/commands/root.js +89 -56
- package/lib/commands/root.js.map +1 -1
- package/lib/commands/sync.js +256 -499
- package/lib/commands/sync.js.map +1 -1
- package/lib/commands/version.js +21 -0
- package/lib/commands/version.js.map +1 -0
- package/lib/commands/whoami.js +15 -11
- package/lib/commands/whoami.js.map +1 -1
- package/lib/main.js +4 -10
- package/lib/main.js.map +1 -1
- package/lib/services/{app.js → app/app.js} +9 -5
- package/lib/services/app/app.js.map +1 -0
- package/lib/services/app/arg.js +28 -0
- package/lib/services/app/arg.js.map +1 -0
- package/lib/services/app/edit-graphql.js +389 -0
- package/lib/services/app/edit-graphql.js.map +1 -0
- package/lib/services/command/arg.js +53 -0
- package/lib/services/command/arg.js.map +1 -0
- package/lib/services/command/command.js +27 -0
- package/lib/services/command/command.js.map +1 -0
- package/lib/services/command/context.js +60 -0
- package/lib/services/command/context.js.map +1 -0
- package/lib/services/{config.js → config/config.js} +32 -35
- package/lib/services/config/config.js.map +1 -0
- package/lib/services/config/env.js +22 -0
- package/lib/services/config/env.js.map +1 -0
- package/lib/services/config/package-json.js +9 -0
- package/lib/services/config/package-json.js.map +1 -0
- package/lib/services/filesync/changes.js +97 -0
- package/lib/services/filesync/changes.js.map +1 -0
- package/lib/services/filesync/conflicts.js +137 -0
- package/lib/services/filesync/conflicts.js.map +1 -0
- package/lib/services/filesync/directory.js +253 -0
- package/lib/services/filesync/directory.js.map +1 -0
- package/lib/services/filesync/error.js +67 -0
- package/lib/services/filesync/error.js.map +1 -0
- package/lib/services/filesync/file.js +3 -0
- package/lib/services/filesync/file.js.map +1 -0
- package/lib/services/filesync/filesync.js +675 -0
- package/lib/services/filesync/filesync.js.map +1 -0
- package/lib/services/filesync/hashes.js +150 -0
- package/lib/services/filesync/hashes.js.map +1 -0
- package/lib/services/http/auth.js +41 -0
- package/lib/services/http/auth.js.map +1 -0
- package/lib/services/http/http.js +64 -0
- package/lib/services/http/http.js.map +1 -0
- package/lib/services/output/log/field.js +3 -0
- package/lib/services/output/log/field.js.map +1 -0
- package/lib/services/output/log/format/format.js +8 -0
- package/lib/services/output/log/format/format.js.map +1 -0
- package/lib/services/output/log/format/json.js +45 -0
- package/lib/services/output/log/format/json.js.map +1 -0
- package/lib/services/output/log/format/pretty.js +147 -0
- package/lib/services/output/log/format/pretty.js.map +1 -0
- package/lib/services/output/log/level.js +41 -0
- package/lib/services/output/log/level.js.map +1 -0
- package/lib/services/output/log/logger.js +40 -0
- package/lib/services/output/log/logger.js.map +1 -0
- package/lib/services/output/log/printer.js +120 -0
- package/lib/services/output/log/printer.js.map +1 -0
- package/lib/services/output/log/structured.js +52 -0
- package/lib/services/output/log/structured.js.map +1 -0
- package/lib/services/{notify.js → output/notify.js} +7 -6
- package/lib/services/output/notify.js.map +1 -0
- package/lib/services/output/prompt.js +52 -0
- package/lib/services/output/prompt.js.map +1 -0
- package/lib/services/output/report.js +162 -0
- package/lib/services/output/report.js.map +1 -0
- package/lib/services/output/sprint.js +21 -0
- package/lib/services/output/sprint.js.map +1 -0
- package/lib/services/{output.js → output/stream.js} +18 -23
- package/lib/services/output/stream.js.map +1 -0
- package/lib/services/{version.js → output/update.js} +26 -18
- package/lib/services/output/update.js.map +1 -0
- package/lib/services/user/session.js +50 -0
- package/lib/services/user/session.js.map +1 -0
- package/lib/services/{user.js → user/user.js} +24 -17
- package/lib/services/user/user.js.map +1 -0
- package/lib/services/util/boolean.js +15 -0
- package/lib/services/util/boolean.js.map +1 -0
- package/lib/services/util/collection.js +38 -0
- package/lib/services/util/collection.js.map +1 -0
- package/lib/services/util/function.js +97 -0
- package/lib/services/util/function.js.map +1 -0
- package/lib/services/util/is.js +46 -0
- package/lib/services/util/is.js.map +1 -0
- package/lib/services/util/number.js +27 -0
- package/lib/services/util/number.js.map +1 -0
- package/lib/services/util/object.js +101 -0
- package/lib/services/util/object.js.map +1 -0
- package/lib/services/util/paths.js +36 -0
- package/lib/services/util/paths.js.map +1 -0
- package/lib/services/{promise.js → util/promise.js} +4 -4
- package/lib/services/util/promise.js.map +1 -0
- package/npm-shrinkwrap.json +2416 -1547
- package/package.json +52 -46
- package/lib/commands/index.js +0 -9
- package/lib/commands/index.js.map +0 -1
- package/lib/services/app.js.map +0 -1
- package/lib/services/args.js +0 -28
- package/lib/services/args.js.map +0 -1
- package/lib/services/config.js.map +0 -1
- package/lib/services/edit-graphql.js +0 -193
- package/lib/services/edit-graphql.js.map +0 -1
- package/lib/services/errors.js +0 -274
- package/lib/services/errors.js.map +0 -1
- package/lib/services/filesync.js +0 -404
- package/lib/services/filesync.js.map +0 -1
- package/lib/services/fs-utils.js +0 -33
- package/lib/services/fs-utils.js.map +0 -1
- package/lib/services/http.js +0 -53
- package/lib/services/http.js.map +0 -1
- package/lib/services/log.js +0 -45
- package/lib/services/log.js.map +0 -1
- package/lib/services/notify.js.map +0 -1
- package/lib/services/output.js.map +0 -1
- package/lib/services/promise.js.map +0 -1
- package/lib/services/session.js +0 -27
- package/lib/services/session.js.map +0 -1
- package/lib/services/sleep.js +0 -19
- package/lib/services/sleep.js.map +0 -1
- package/lib/services/timeout.js +0 -8
- package/lib/services/timeout.js.map +0 -1
- package/lib/services/user.js.map +0 -1
- 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 @@
|
|
|
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"}
|