@gadgetinc/ggt 0.4.9 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +165 -93
- package/lib/__generated__/graphql.js +66 -1
- package/lib/__generated__/graphql.js.map +1 -1
- package/lib/commands/deploy.js +329 -184
- package/lib/commands/deploy.js.map +1 -1
- package/lib/commands/dev.js +445 -0
- package/lib/commands/dev.js.map +1 -0
- package/lib/commands/list.js +27 -19
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/login.js +15 -11
- package/lib/commands/login.js.map +1 -1
- package/lib/commands/logout.js +5 -5
- package/lib/commands/logout.js.map +1 -1
- package/lib/commands/open.js +200 -0
- package/lib/commands/open.js.map +1 -0
- package/lib/commands/pull.js +128 -0
- package/lib/commands/pull.js.map +1 -0
- package/lib/commands/push.js +126 -0
- package/lib/commands/push.js.map +1 -0
- package/lib/commands/root.js +46 -28
- package/lib/commands/root.js.map +1 -1
- package/lib/commands/status.js +61 -0
- package/lib/commands/status.js.map +1 -0
- package/lib/commands/version.js +6 -6
- package/lib/commands/version.js.map +1 -1
- package/lib/commands/whoami.js +6 -6
- package/lib/commands/whoami.js.map +1 -1
- package/lib/ggt.js +33 -8
- package/lib/ggt.js.map +1 -1
- package/lib/main.js +5 -0
- package/lib/main.js.map +1 -0
- package/lib/services/app/api/api.js +191 -0
- package/lib/services/app/api/api.js.map +1 -0
- package/lib/services/app/api/operation.js +12 -0
- package/lib/services/app/api/operation.js.map +1 -0
- package/lib/services/app/app.js +44 -10
- package/lib/services/app/app.js.map +1 -1
- package/lib/services/app/{edit/client.js → client.js} +29 -19
- package/lib/services/app/client.js.map +1 -0
- package/lib/services/app/edit/edit.js +67 -31
- package/lib/services/app/edit/edit.js.map +1 -1
- package/lib/services/app/edit/operation.js +19 -3
- package/lib/services/app/edit/operation.js.map +1 -1
- package/lib/services/app/{edit/error.js → error.js} +6 -6
- package/lib/services/app/error.js.map +1 -0
- package/lib/services/command/arg.js +4 -4
- package/lib/services/command/arg.js.map +1 -1
- package/lib/services/command/command.js +9 -7
- package/lib/services/command/command.js.map +1 -1
- package/lib/services/command/context.js +82 -20
- package/lib/services/command/context.js.map +1 -1
- package/lib/services/config/config.js +4 -7
- package/lib/services/config/config.js.map +1 -1
- package/lib/services/config/env.js +1 -1
- package/lib/services/config/env.js.map +1 -1
- package/lib/services/filesync/changes.js +76 -37
- package/lib/services/filesync/changes.js.map +1 -1
- package/lib/services/filesync/conflicts.js +10 -9
- package/lib/services/filesync/conflicts.js.map +1 -1
- package/lib/services/filesync/directory.js +16 -1
- package/lib/services/filesync/directory.js.map +1 -1
- package/lib/services/filesync/error.js +96 -27
- package/lib/services/filesync/error.js.map +1 -1
- package/lib/services/filesync/filesync.js +448 -472
- package/lib/services/filesync/filesync.js.map +1 -1
- package/lib/services/filesync/hashes.js +8 -5
- package/lib/services/filesync/hashes.js.map +1 -1
- package/lib/services/filesync/strategy.js +59 -0
- package/lib/services/filesync/strategy.js.map +1 -0
- package/lib/services/filesync/sync-json.js +475 -0
- package/lib/services/filesync/sync-json.js.map +1 -0
- package/lib/services/http/auth.js +30 -1
- package/lib/services/http/auth.js.map +1 -1
- package/lib/services/http/http.js +5 -0
- package/lib/services/http/http.js.map +1 -1
- package/lib/services/output/confirm.js +149 -0
- package/lib/services/output/confirm.js.map +1 -0
- package/lib/services/output/footer.js +22 -0
- package/lib/services/output/footer.js.map +1 -0
- package/lib/services/output/log/format/pretty.js +2 -1
- package/lib/services/output/log/format/pretty.js.map +1 -1
- package/lib/services/output/log/logger.js +13 -5
- package/lib/services/output/log/logger.js.map +1 -1
- package/lib/services/output/log/structured.js +2 -2
- package/lib/services/output/log/structured.js.map +1 -1
- package/lib/services/output/output.js +197 -0
- package/lib/services/output/output.js.map +1 -0
- package/lib/services/output/print.js +31 -0
- package/lib/services/output/print.js.map +1 -0
- package/lib/services/output/problems.js +84 -0
- package/lib/services/output/problems.js.map +1 -0
- package/lib/services/output/prompt.js +173 -40
- package/lib/services/output/prompt.js.map +1 -1
- package/lib/services/output/report.js +63 -19
- package/lib/services/output/report.js.map +1 -1
- package/lib/services/output/select.js +198 -0
- package/lib/services/output/select.js.map +1 -0
- package/lib/services/output/spinner.js +141 -0
- package/lib/services/output/spinner.js.map +1 -0
- package/lib/services/output/sprint.js +38 -15
- package/lib/services/output/sprint.js.map +1 -1
- package/lib/services/output/symbols.js +23 -0
- package/lib/services/output/symbols.js.map +1 -0
- package/lib/services/output/table.js +98 -0
- package/lib/services/output/table.js.map +1 -0
- package/lib/services/output/timestamp.js +12 -0
- package/lib/services/output/timestamp.js.map +1 -0
- package/lib/services/output/update.js +29 -9
- package/lib/services/output/update.js.map +1 -1
- package/lib/services/user/session.js +4 -0
- package/lib/services/user/session.js.map +1 -1
- package/lib/services/user/user.js +15 -10
- package/lib/services/user/user.js.map +1 -1
- package/lib/services/util/assert.js +11 -0
- package/lib/services/util/assert.js.map +1 -0
- package/lib/services/util/boolean.js +2 -2
- package/lib/services/util/boolean.js.map +1 -1
- package/lib/services/util/function.js +45 -7
- package/lib/services/util/function.js.map +1 -1
- package/lib/services/util/is.js +23 -2
- package/lib/services/util/is.js.map +1 -1
- package/lib/services/util/json.js +16 -13
- package/lib/services/util/json.js.map +1 -1
- package/lib/services/util/object.js +2 -2
- package/lib/services/util/object.js.map +1 -1
- package/lib/services/util/promise.js +5 -2
- package/lib/services/util/promise.js.map +1 -1
- package/lib/services/util/types.js.map +1 -1
- package/npm-shrinkwrap.json +3436 -2833
- package/package.json +47 -40
- package/bin/dev.cmd +0 -3
- package/bin/dev.js +0 -14
- package/bin/run.cmd +0 -3
- package/bin/run.js +0 -5
- package/lib/commands/sync.js +0 -284
- package/lib/commands/sync.js.map +0 -1
- package/lib/services/app/edit/client.js.map +0 -1
- package/lib/services/app/edit/error.js.map +0 -1
- package/lib/services/output/log/printer.js +0 -120
- package/lib/services/output/log/printer.js.map +0 -1
- package/lib/services/output/stream.js +0 -54
- package/lib/services/output/stream.js.map +0 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
+
import { printTable } from "../output/table.js";
|
|
2
3
|
import { ChangesWithHash, isEqualHash } from "./hashes.js";
|
|
3
4
|
/**
|
|
4
5
|
* A map of conflicting changes made between the user's local filesystem
|
|
@@ -9,10 +10,10 @@ import { ChangesWithHash, isEqualHash } from "./hashes.js";
|
|
|
9
10
|
/**
|
|
10
11
|
* Returns the conflicting changes between the user's local filesystem
|
|
11
12
|
* and Gadget's filesystem.
|
|
12
|
-
*/ export const getConflicts = ({ localChanges,
|
|
13
|
+
*/ export const getConflicts = ({ localChanges, environmentChanges })=>{
|
|
13
14
|
const conflicts = new Conflicts();
|
|
14
15
|
for (const [filepath, localChange] of localChanges){
|
|
15
|
-
const gadgetChange =
|
|
16
|
+
const gadgetChange = environmentChanges.get(filepath);
|
|
16
17
|
if (!gadgetChange) {
|
|
17
18
|
continue;
|
|
18
19
|
}
|
|
@@ -58,16 +59,17 @@ import { ChangesWithHash, isEqualHash } from "./hashes.js";
|
|
|
58
59
|
/**
|
|
59
60
|
* Prints a table of conflicts between local changes and gadget changes.
|
|
60
61
|
*
|
|
61
|
-
* @param ctx - The current context.
|
|
62
62
|
* @param options - The options to use.
|
|
63
|
-
* @param options.message - The message to print above the table.
|
|
64
63
|
* @param options.conflicts - The conflicts to print.
|
|
65
|
-
*/
|
|
64
|
+
*/ // TODO: write a snapshot test for this!
|
|
65
|
+
export const printConflicts = ({ conflicts })=>{
|
|
66
66
|
const created = chalk.greenBright("+ created");
|
|
67
67
|
const updated = chalk.blueBright("± updated");
|
|
68
68
|
const deleted = chalk.redBright("- deleted");
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
printTable({
|
|
70
|
+
title: "These files have conflicting changes.",
|
|
71
|
+
ensureEmptyLineAbove: true,
|
|
72
|
+
ensureEmptyLineAboveBody: true,
|
|
71
73
|
colAligns: [
|
|
72
74
|
"left",
|
|
73
75
|
"center",
|
|
@@ -76,9 +78,8 @@ import { ChangesWithHash, isEqualHash } from "./hashes.js";
|
|
|
76
78
|
headers: [
|
|
77
79
|
"",
|
|
78
80
|
"You",
|
|
79
|
-
"
|
|
81
|
+
"Environment"
|
|
80
82
|
],
|
|
81
|
-
spaceY: 1,
|
|
82
83
|
rows: Array.from(conflicts.entries()).sort((a, b)=>a[0].localeCompare(b[0])).map(([path, { localChange, gadgetChange }])=>{
|
|
83
84
|
switch(true){
|
|
84
85
|
case localChange.type === "create" && gadgetChange.type === "create":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/services/filesync/conflicts.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport
|
|
1
|
+
{"version":3,"sources":["../../../src/services/filesync/conflicts.ts"],"sourcesContent":["import chalk from \"chalk\";\nimport { printTable } from \"../output/table.js\";\nimport { ChangesWithHash, isEqualHash, type ChangeWithHash } from \"./hashes.js\";\n\n/**\n * A map of conflicting changes made between the user's local filesystem\n * and Gadget's filesystem where the key is the path of the conflicting\n * file and the value is an object containing the conflicting changes.\n */\nexport class Conflicts extends Map<string, { localChange: ChangeWithHash; gadgetChange: ChangeWithHash }> {}\n\n/**\n * Returns the conflicting changes between the user's local filesystem\n * and Gadget's filesystem.\n */\nexport const getConflicts = ({\n localChanges,\n environmentChanges,\n}: {\n localChanges: ChangesWithHash;\n environmentChanges: ChangesWithHash;\n}): Conflicts => {\n const conflicts = new Conflicts();\n\n for (const [filepath, localChange] of localChanges) {\n const gadgetChange = environmentChanges.get(filepath);\n if (!gadgetChange) {\n // gadget doesn't have this change, so there's no conflict\n continue;\n }\n\n if (localChange.type === \"delete\" && gadgetChange.type === \"delete\") {\n // local and gadget both deleted the same file\n continue;\n }\n\n if (\n \"targetHash\" in localChange &&\n \"targetHash\" in gadgetChange &&\n isEqualHash(filepath, localChange.targetHash, gadgetChange.targetHash)\n ) {\n // local and gadget both created/updated the same file with the same content\n continue;\n }\n\n // local and gadget both updated the same file with different\n // content or one updated and the other deleted\n conflicts.set(filepath, { localChange, gadgetChange });\n }\n\n // ignore .gadget/ file conflicts and always use gadget's version\n // since gadget is the source of truth for .gadget/ files\n for (const filepath of conflicts.keys()) {\n if (filepath.startsWith(\".gadget/\")) {\n conflicts.delete(filepath);\n }\n }\n\n return conflicts;\n};\n\n/**\n * Returns a new `Changes` object that contains only the changes that do\n * not have conflicts.\n *\n * @param options - The options to use.\n * @param options.conflicts - The conflicts to check against.\n * @param options.changes - The changes to filter.\n * @returns A new {@linkcode Changes} object without conflicts.\n */\nexport const withoutConflictingChanges = ({ conflicts, changes }: { conflicts: Conflicts; changes: ChangesWithHash }): ChangesWithHash => {\n const changesWithoutConflicts = new ChangesWithHash(changes);\n\n for (const [filepath] of changesWithoutConflicts) {\n if (conflicts.has(filepath)) {\n changesWithoutConflicts.delete(filepath);\n }\n }\n\n return changesWithoutConflicts;\n};\n\n/**\n * Prints a table of conflicts between local changes and gadget changes.\n *\n * @param options - The options to use.\n * @param options.conflicts - The conflicts to print.\n */\n// TODO: write a snapshot test for this!\nexport const printConflicts = ({ conflicts }: { conflicts: Conflicts }): void => {\n const created = chalk.greenBright(\"+ created\");\n const updated = chalk.blueBright(\"± updated\");\n const deleted = chalk.redBright(\"- deleted\");\n\n printTable({\n title: \"These files have conflicting changes.\",\n ensureEmptyLineAbove: true,\n ensureEmptyLineAboveBody: true,\n colAligns: [\"left\", \"center\", \"center\"],\n headers: [\"\", \"You\", \"Environment\"],\n rows: Array.from(conflicts.entries())\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([path, { localChange, gadgetChange }]) => {\n switch (true) {\n case localChange.type === \"create\" && gadgetChange.type === \"create\":\n return [path, created, created];\n case localChange.type === \"create\" && gadgetChange.type === \"update\":\n return [path, created, updated];\n case localChange.type === \"create\" && gadgetChange.type === \"delete\":\n return [path, created, deleted];\n case localChange.type === \"update\" && gadgetChange.type === \"create\":\n return [path, updated, created];\n case localChange.type === \"update\" && gadgetChange.type === \"update\":\n return [path, updated, updated];\n case localChange.type === \"update\" && gadgetChange.type === \"delete\":\n return [path, updated, deleted];\n case localChange.type === \"delete\" && gadgetChange.type === \"create\":\n return [path, deleted, created];\n case localChange.type === \"delete\" && gadgetChange.type === \"update\":\n return [path, deleted, updated];\n default:\n throw new Error(`Unexpected conflict: ${localChange.type} vs ${gadgetChange.type}`);\n }\n }),\n });\n};\n"],"names":["chalk","printTable","ChangesWithHash","isEqualHash","Conflicts","Map","getConflicts","localChanges","environmentChanges","conflicts","filepath","localChange","gadgetChange","get","type","targetHash","set","keys","startsWith","delete","withoutConflictingChanges","changes","changesWithoutConflicts","has","printConflicts","created","greenBright","updated","blueBright","deleted","redBright","title","ensureEmptyLineAbove","ensureEmptyLineAboveBody","colAligns","headers","rows","Array","from","entries","sort","a","b","localeCompare","map","path","Error"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DO NOT MODIFY
|
|
3
3
|
*
|
|
4
|
-
* Everything in this file also exists in
|
|
4
|
+
* Everything in this file also exists in gadget to ensure that this logic
|
|
5
5
|
* is the same between the two projects.
|
|
6
6
|
*/ import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
7
7
|
import fs from "fs-extra";
|
|
@@ -167,6 +167,21 @@ import normalizePath from "normalize-path";
|
|
|
167
167
|
this._isHashing = false;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
+
async hasFiles() {
|
|
171
|
+
return !await this.isEmptyOrNonExistent();
|
|
172
|
+
}
|
|
173
|
+
async isEmptyOrNonExistent() {
|
|
174
|
+
let isEmptyOrNonExistent = true;
|
|
175
|
+
try {
|
|
176
|
+
for await (const _ of this.walk()){
|
|
177
|
+
isEmptyOrNonExistent = false;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
swallowEnoent(error);
|
|
182
|
+
}
|
|
183
|
+
return isEmptyOrNonExistent;
|
|
184
|
+
}
|
|
170
185
|
constructor(/**
|
|
171
186
|
* An absolute path to the directory that is being synced.
|
|
172
187
|
*/ path){
|
|
@@ -1 +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\", \"yarn-error.log\"] 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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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/services/filesync/directory.ts"],"sourcesContent":["/**\n * DO NOT MODIFY\n *\n * Everything in this file also exists in gadget 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\", \"yarn-error.log\"] 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 async hasFiles(): Promise<boolean> {\n return !(await this.isEmptyOrNonExistent());\n }\n\n async isEmptyOrNonExistent(): Promise<boolean> {\n let isEmptyOrNonExistent = true;\n try {\n for await (const _ of this.walk()) {\n isEmptyOrNonExistent = false;\n break;\n }\n } catch (error) {\n swallowEnoent(error);\n }\n return isEmptyOrNonExistent;\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","hasFiles","isEmptyOrNonExistent","_","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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"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { ClientError } from "../app/error.js";
|
|
3
|
+
import { sprintProblems } from "../output/problems.js";
|
|
4
|
+
import { GGTError, IsBug } from "../output/report.js";
|
|
5
|
+
import { sprint, sprintln } from "../output/sprint.js";
|
|
6
|
+
import { isGraphQLErrors, isGraphQLResult, isObject, isString } from "../util/is.js";
|
|
7
|
+
export class YarnNotFoundError extends GGTError {
|
|
5
8
|
render() {
|
|
6
9
|
return sprint`
|
|
7
10
|
Yarn must be installed to sync your application. You can install it by running:
|
|
@@ -16,52 +19,118 @@ export class YarnNotFoundError extends CLIError {
|
|
|
16
19
|
_define_property(this, "isBug", IsBug.NO);
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
|
-
export class
|
|
22
|
+
export class UnknownDirectoryError extends GGTError {
|
|
20
23
|
render() {
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
const cmd = this.ctx.command;
|
|
25
|
+
const dir = this.opts.directory.path;
|
|
26
|
+
switch(cmd){
|
|
27
|
+
case "open":
|
|
28
|
+
case "status":
|
|
29
|
+
return sprint`
|
|
30
|
+
A ".gadget/sync.json" file is missing in this directory:
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
${dir}
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
In order to use "ggt ${cmd}", you must run it within a directory
|
|
35
|
+
that has already been initialized with "ggt dev".
|
|
36
|
+
`;
|
|
37
|
+
case "dev":
|
|
38
|
+
return sprint`
|
|
39
|
+
A ".gadget/sync.json" file is missing in this directory:
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
${dir}
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
43
|
+
If you're running "ggt dev" for the first time, we recommend
|
|
44
|
+
using a gadget specific directory like this:
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
ggt dev ~/gadget/${this.ctx.args["--app"] ?? "<name>"}
|
|
47
|
+
|
|
48
|
+
To use a non-empty directory without a ".gadget/sync.json" file,
|
|
49
|
+
run "ggt dev" again with the "--allow-unknown-directory" flag:
|
|
50
|
+
|
|
51
|
+
ggt dev ${dir} --allow-unknown-directory
|
|
52
|
+
`;
|
|
53
|
+
default:
|
|
54
|
+
return sprint`
|
|
55
|
+
A ".gadget/sync.json" file is missing in this directory:
|
|
56
|
+
|
|
57
|
+
${dir}
|
|
58
|
+
|
|
59
|
+
If you're certain you want to use this directory, you can run
|
|
60
|
+
"ggt ${cmd}" again with the "--allow-unknown-directory" flag:
|
|
61
|
+
|
|
62
|
+
ggt ${cmd} --allow-unknown-directory
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
36
65
|
}
|
|
37
|
-
constructor(
|
|
38
|
-
super(
|
|
39
|
-
_define_property(this, "
|
|
40
|
-
_define_property(this, "
|
|
66
|
+
constructor(ctx, opts){
|
|
67
|
+
super('The ".gadget/sync.json" file was invalid or not found');
|
|
68
|
+
_define_property(this, "ctx", void 0);
|
|
69
|
+
_define_property(this, "opts", void 0);
|
|
41
70
|
_define_property(this, "isBug", void 0);
|
|
42
|
-
this.
|
|
43
|
-
this.
|
|
71
|
+
this.ctx = ctx;
|
|
72
|
+
this.opts = opts;
|
|
44
73
|
this.isBug = IsBug.NO;
|
|
45
|
-
this.app ??= "<name of app>";
|
|
46
74
|
}
|
|
47
75
|
}
|
|
48
|
-
export class
|
|
76
|
+
export class TooManyMergeAttemptsError extends GGTError {
|
|
49
77
|
render() {
|
|
50
78
|
return sprint`
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
We merged your local files with your environment's files ${this.attempts} times,
|
|
80
|
+
but your local and environment's files still don't match.
|
|
53
81
|
|
|
54
|
-
|
|
55
|
-
and try again.
|
|
82
|
+
Make sure no one else is editing files on your environment, and try again.
|
|
56
83
|
`;
|
|
57
84
|
}
|
|
58
85
|
constructor(attempts){
|
|
59
|
-
super(`Failed to
|
|
86
|
+
super(`Failed to synchronize files after ${attempts} attempts.`);
|
|
60
87
|
_define_property(this, "attempts", void 0);
|
|
61
88
|
_define_property(this, "isBug", void 0);
|
|
62
89
|
this.attempts = attempts;
|
|
63
90
|
this.isBug = IsBug.MAYBE;
|
|
64
91
|
}
|
|
65
92
|
}
|
|
93
|
+
export class DeployDisallowedError extends GGTError {
|
|
94
|
+
render() {
|
|
95
|
+
let output = sprintln`{red Gadget has detected the following fatal errors with your files:}`;
|
|
96
|
+
output += sprintProblems({
|
|
97
|
+
ensureEmptyLineAbove: true,
|
|
98
|
+
problems: this.fatalErrors,
|
|
99
|
+
showFileTypes: false
|
|
100
|
+
});
|
|
101
|
+
output += sprintln({
|
|
102
|
+
ensureEmptyLineAbove: true
|
|
103
|
+
})`{red Please fix these errors and try again.}`;
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
constructor(fatalErrors){
|
|
107
|
+
super("This application is not allowed to be deployed due to fatal errors.");
|
|
108
|
+
_define_property(this, "fatalErrors", void 0);
|
|
109
|
+
_define_property(this, "isBug", void 0);
|
|
110
|
+
this.fatalErrors = fatalErrors;
|
|
111
|
+
this.isBug = IsBug.MAYBE;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export const isFilesVersionMismatchError = (error)=>{
|
|
115
|
+
if (error instanceof ClientError) {
|
|
116
|
+
error = error.cause;
|
|
117
|
+
}
|
|
118
|
+
if (isGraphQLResult(error)) {
|
|
119
|
+
error = error.errors;
|
|
120
|
+
}
|
|
121
|
+
if (isGraphQLErrors(error)) {
|
|
122
|
+
error = error[0];
|
|
123
|
+
}
|
|
124
|
+
return isObject(error) && "message" in error && isString(error.message) && error.message.includes("Files version mismatch");
|
|
125
|
+
};
|
|
126
|
+
export const swallowFilesVersionMismatch = (ctx, error)=>{
|
|
127
|
+
if (isFilesVersionMismatchError(error)) {
|
|
128
|
+
ctx.log.debug("swallowing files version mismatch", {
|
|
129
|
+
error
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
};
|
|
66
135
|
|
|
67
136
|
//# sourceMappingURL=error.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/services/filesync/error.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"sources":["../../../src/services/filesync/error.ts"],"sourcesContent":["import { ClientError } from \"../app/error.js\";\nimport type { Context } from \"../command/context.js\";\nimport { sprintProblems, type Problems } from \"../output/problems.js\";\nimport { GGTError, IsBug } from \"../output/report.js\";\nimport { sprint, sprintln } from \"../output/sprint.js\";\nimport { isGraphQLErrors, isGraphQLResult, isObject, isString } from \"../util/is.js\";\nimport type { Directory } from \"./directory.js\";\nimport type { SyncJsonArgs } from \"./sync-json.js\";\n\nexport class YarnNotFoundError extends GGTError {\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 UnknownDirectoryError extends GGTError {\n isBug = IsBug.NO;\n\n constructor(\n readonly ctx: Context<SyncJsonArgs>,\n readonly opts: { directory: Directory },\n ) {\n super('The \".gadget/sync.json\" file was invalid or not found');\n }\n\n protected render(): string {\n const cmd = this.ctx.command;\n const dir = this.opts.directory.path;\n\n switch (cmd) {\n case \"open\":\n case \"status\":\n return sprint`\n A \".gadget/sync.json\" file is missing in this directory:\n\n ${dir}\n\n In order to use \"ggt ${cmd}\", you must run it within a directory\n that has already been initialized with \"ggt dev\".\n `;\n case \"dev\":\n return sprint`\n A \".gadget/sync.json\" file is missing in this directory:\n\n ${dir}\n\n If you're running \"ggt dev\" for the first time, we recommend\n using a gadget specific directory like this:\n\n ggt dev ~/gadget/${this.ctx.args[\"--app\"] ?? \"<name>\"}\n\n To use a non-empty directory without a \".gadget/sync.json\" file,\n run \"ggt dev\" again with the \"--allow-unknown-directory\" flag:\n\n ggt dev ${dir} --allow-unknown-directory\n `;\n default:\n return sprint`\n A \".gadget/sync.json\" file is missing in this directory:\n\n ${dir}\n\n If you're certain you want to use this directory, you can run\n \"ggt ${cmd}\" again with the \"--allow-unknown-directory\" flag:\n\n ggt ${cmd} --allow-unknown-directory\n `;\n }\n }\n}\n\nexport class TooManyMergeAttemptsError extends GGTError {\n isBug = IsBug.MAYBE;\n\n constructor(readonly attempts: number) {\n super(`Failed to synchronize files after ${attempts} attempts.`);\n }\n\n protected render(): string {\n return sprint`\n We merged your local files with your environment's files ${this.attempts} times,\n but your local and environment's files still don't match.\n\n Make sure no one else is editing files on your environment, and try again.\n `;\n }\n}\n\nexport class DeployDisallowedError extends GGTError {\n isBug = IsBug.MAYBE;\n\n constructor(readonly fatalErrors: Problems) {\n super(\"This application is not allowed to be deployed due to fatal errors.\");\n }\n\n protected render(): string {\n let output = sprintln`{red Gadget has detected the following fatal errors with your files:}`;\n output += sprintProblems({ ensureEmptyLineAbove: true, problems: this.fatalErrors, showFileTypes: false });\n output += sprintln({ ensureEmptyLineAbove: true })`{red Please fix these errors and try again.}`;\n return output;\n }\n}\n\nexport const isFilesVersionMismatchError = (error: unknown): boolean => {\n if (error instanceof ClientError) {\n error = error.cause;\n }\n if (isGraphQLResult(error)) {\n error = error.errors;\n }\n if (isGraphQLErrors(error)) {\n error = error[0];\n }\n return isObject(error) && \"message\" in error && isString(error.message) && error.message.includes(\"Files version mismatch\");\n};\n\nexport const swallowFilesVersionMismatch = (ctx: Context, error: unknown): void => {\n if (isFilesVersionMismatchError(error)) {\n ctx.log.debug(\"swallowing files version mismatch\", { error });\n return;\n }\n throw error;\n};\n"],"names":["ClientError","sprintProblems","GGTError","IsBug","sprint","sprintln","isGraphQLErrors","isGraphQLResult","isObject","isString","YarnNotFoundError","render","constructor","isBug","NO","UnknownDirectoryError","cmd","ctx","command","dir","opts","directory","path","args","TooManyMergeAttemptsError","attempts","MAYBE","DeployDisallowedError","output","ensureEmptyLineAbove","problems","fatalErrors","showFileTypes","isFilesVersionMismatchError","error","cause","errors","message","includes","swallowFilesVersionMismatch","log","debug"],"mappings":"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"}
|