@gadgetinc/ggt 1.0.1 → 1.0.3

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 (100) hide show
  1. package/README.md +192 -128
  2. package/lib/__generated__/graphql.js.map +1 -1
  3. package/lib/commands/add.js +385 -0
  4. package/lib/commands/add.js.map +1 -0
  5. package/lib/commands/deploy.js +20 -102
  6. package/lib/commands/deploy.js.map +1 -1
  7. package/lib/commands/dev.js +43 -114
  8. package/lib/commands/dev.js.map +1 -1
  9. package/lib/commands/list.js +3 -6
  10. package/lib/commands/list.js.map +1 -1
  11. package/lib/commands/login.js +2 -5
  12. package/lib/commands/login.js.map +1 -1
  13. package/lib/commands/logout.js +2 -5
  14. package/lib/commands/logout.js.map +1 -1
  15. package/lib/commands/open.js +45 -86
  16. package/lib/commands/open.js.map +1 -1
  17. package/lib/commands/pull.js +19 -76
  18. package/lib/commands/pull.js.map +1 -1
  19. package/lib/commands/push.js +19 -76
  20. package/lib/commands/push.js.map +1 -1
  21. package/lib/commands/root.js +4 -3
  22. package/lib/commands/root.js.map +1 -1
  23. package/lib/commands/status.js +3 -8
  24. package/lib/commands/status.js.map +1 -1
  25. package/lib/commands/version.js +6 -5
  26. package/lib/commands/version.js.map +1 -1
  27. package/lib/commands/whoami.js +2 -5
  28. package/lib/commands/whoami.js.map +1 -1
  29. package/lib/ggt.js.map +1 -1
  30. package/lib/main.js.map +1 -1
  31. package/lib/services/app/api/api.js.map +1 -1
  32. package/lib/services/app/api/operation.js +11 -0
  33. package/lib/services/app/api/operation.js.map +1 -1
  34. package/lib/services/app/app.js +21 -2
  35. package/lib/services/app/app.js.map +1 -1
  36. package/lib/services/app/arg.js.map +1 -1
  37. package/lib/services/app/client.js +1 -5
  38. package/lib/services/app/client.js.map +1 -1
  39. package/lib/services/app/edit/edit.js.map +1 -1
  40. package/lib/services/app/edit/operation.js +52 -0
  41. package/lib/services/app/edit/operation.js.map +1 -1
  42. package/lib/services/app/error.js.map +1 -1
  43. package/lib/services/command/arg.js +3 -1
  44. package/lib/services/command/arg.js.map +1 -1
  45. package/lib/services/command/command.js +1 -0
  46. package/lib/services/command/command.js.map +1 -1
  47. package/lib/services/command/context.js.map +1 -1
  48. package/lib/services/config/config.js.map +1 -1
  49. package/lib/services/config/env.js.map +1 -1
  50. package/lib/services/config/package-json.js.map +1 -1
  51. package/lib/services/filesync/changes.js.map +1 -1
  52. package/lib/services/filesync/conflicts.js.map +1 -1
  53. package/lib/services/filesync/directory.js.map +1 -1
  54. package/lib/services/filesync/error.js +1 -0
  55. package/lib/services/filesync/error.js.map +1 -1
  56. package/lib/services/filesync/file.js.map +1 -1
  57. package/lib/services/filesync/filesync.js +179 -174
  58. package/lib/services/filesync/filesync.js.map +1 -1
  59. package/lib/services/filesync/hashes.js.map +1 -1
  60. package/lib/services/filesync/strategy.js.map +1 -1
  61. package/lib/services/filesync/sync-json.js.map +1 -1
  62. package/lib/services/http/auth.js.map +1 -1
  63. package/lib/services/http/http.js.map +1 -1
  64. package/lib/services/output/confirm.js.map +1 -1
  65. package/lib/services/output/footer.js.map +1 -1
  66. package/lib/services/output/log/field.js.map +1 -1
  67. package/lib/services/output/log/format/format.js.map +1 -1
  68. package/lib/services/output/log/format/json.js.map +1 -1
  69. package/lib/services/output/log/format/pretty.js.map +1 -1
  70. package/lib/services/output/log/level.js.map +1 -1
  71. package/lib/services/output/log/logger.js.map +1 -1
  72. package/lib/services/output/log/structured.js.map +1 -1
  73. package/lib/services/output/notify.js.map +1 -1
  74. package/lib/services/output/output.js.map +1 -1
  75. package/lib/services/output/print.js.map +1 -1
  76. package/lib/services/output/problems.js.map +1 -1
  77. package/lib/services/output/prompt.js.map +1 -1
  78. package/lib/services/output/report.js.map +1 -1
  79. package/lib/services/output/select.js.map +1 -1
  80. package/lib/services/output/spinner.js.map +1 -1
  81. package/lib/services/output/sprint.js.map +1 -1
  82. package/lib/services/output/symbols.js.map +1 -1
  83. package/lib/services/output/table.js.map +1 -1
  84. package/lib/services/output/timestamp.js.map +1 -1
  85. package/lib/services/output/update.js.map +1 -1
  86. package/lib/services/user/session.js.map +1 -1
  87. package/lib/services/user/user.js.map +1 -1
  88. package/lib/services/util/assert.js.map +1 -1
  89. package/lib/services/util/boolean.js.map +1 -1
  90. package/lib/services/util/collection.js.map +1 -1
  91. package/lib/services/util/function.js.map +1 -1
  92. package/lib/services/util/is.js.map +1 -1
  93. package/lib/services/util/json.js.map +1 -1
  94. package/lib/services/util/number.js.map +1 -1
  95. package/lib/services/util/object.js.map +1 -1
  96. package/lib/services/util/paths.js.map +1 -1
  97. package/lib/services/util/promise.js.map +1 -1
  98. package/lib/services/util/types.js.map +1 -1
  99. package/npm-shrinkwrap.json +2071 -1684
  100. package/package.json +30 -30
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/filesync/filesync.ts"],"sourcesContent":["import { execa } from \"execa\";\nimport fs from \"fs-extra\";\nimport ms from \"ms\";\nimport assert from \"node:assert\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport pMap from \"p-map\";\nimport PQueue from \"p-queue\";\nimport pRetry from \"p-retry\";\nimport pluralize from \"pluralize\";\nimport type { Promisable } from \"type-fest\";\nimport { FileSyncEncoding, type FileSyncChangedEventInput, type FileSyncDeletedEventInput } from \"../../__generated__/graphql.js\";\nimport type { DevArgs } from \"../../commands/dev.js\";\nimport type { PullArgs } from \"../../commands/pull.js\";\nimport { type EditSubscription } from \"../app/edit/edit.js\";\nimport {\n FILE_SYNC_COMPARISON_HASHES_QUERY,\n FILE_SYNC_FILES_QUERY,\n FILE_SYNC_HASHES_QUERY,\n PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n} from \"../app/edit/operation.js\";\nimport type { Context } from \"../command/context.js\";\nimport { config } from \"../config/config.js\";\nimport { confirm } from \"../output/confirm.js\";\nimport { println } from \"../output/print.js\";\nimport { filesyncProblemsToProblems, sprintProblems } from \"../output/problems.js\";\nimport { EdgeCaseError } from \"../output/report.js\";\nimport { select } from \"../output/select.js\";\nimport { spin, type spinner } from \"../output/spinner.js\";\nimport { sprint, sprintln } from \"../output/sprint.js\";\nimport { symbol } from \"../output/symbols.js\";\nimport { ts } from \"../output/timestamp.js\";\nimport { noop } from \"../util/function.js\";\nimport { isEEXISTError, isENOENTError, isENOTDIRError, isENOTEMPTYError } from \"../util/is.js\";\nimport { serializeError } from \"../util/object.js\";\nimport { Changes, printChanges, sprintChanges, type PrintChangesOptions } from \"./changes.js\";\nimport { getConflicts, printConflicts, withoutConflictingChanges } from \"./conflicts.js\";\nimport { supportsPermissions, swallowEnoent, type Hashes } from \"./directory.js\";\nimport { TooManyMergeAttemptsError, isFilesVersionMismatchError, swallowFilesVersionMismatch } from \"./error.js\";\nimport type { File } from \"./file.js\";\nimport { getNecessaryChanges, isEqualHashes, type ChangesWithHash } from \"./hashes.js\";\nimport { MergeConflictPreference } from \"./strategy.js\";\nimport { type SyncJson, type SyncJsonArgs } from \"./sync-json.js\";\n\n/**\n * The maximum attempts to automatically merge local and environment\n * file changes when a FilesVersionMismatchError is encountered before\n * throwing a {@linkcode TooManyMergeAttemptsError}.\n */\nexport const MAX_MERGE_ATTEMPTS = 10;\n\n/**\n * The maximum length of file content that can be pushed to Gadget in a\n * single request.\n */\nexport const MAX_PUSH_CONTENT_LENGTH = 50 * 1024 * 1024; // 50mb\n\nexport type FileSyncHashes = {\n /**\n * Whether the local filesystem is in sync with the environment's\n * filesystem.\n */\n inSync: boolean;\n\n /**\n * Whether the local filesystem and the environment's filesystem have\n * both changed since the last sync.\n */\n bothChanged: boolean;\n\n /**\n * Whether only .gadget/ files have changed on the environment's\n * filesystem.\n */\n onlyDotGadgetFilesChanged: boolean;\n\n /**\n * The hashes of the files at the local filesVersion.\n */\n localFilesVersionHashes: Hashes;\n\n /**\n * The hashes of the files on the local filesystem.\n */\n localHashes: Hashes;\n\n /**\n * The changes the local filesystem has made since the last sync.\n */\n localChanges: ChangesWithHash;\n\n /**\n * The changes the local filesystem needs to push to make the\n * environment's filesystem in sync with the local filesystem.\n *\n * NOTE: If the environment's filesystem has changed since the last\n * sync, these changes will undo those changes.\n */\n localChangesToPush: Changes | ChangesWithHash;\n\n /**\n * The filesVersion of the environment's filesystem.\n */\n environmentFilesVersion: bigint;\n\n /**\n * The hashes of the files on the environment's filesystem.\n */\n environmentHashes: Hashes;\n\n /**\n * The changes the environment's filesystem has made since the last\n * sync.\n */\n environmentChanges: ChangesWithHash;\n\n /**\n * The changes the local filesystem needs to pull from the\n * environment's filesystem to be in sync with the environment's\n * filesystem.\n *\n * NOTE: If the local filesystem has changed since the last sync,\n * these changes will undo those changes.\n */\n environmentChangesToPull: Changes | ChangesWithHash;\n};\n\nexport class FileSync {\n /**\n * A FIFO async callback queue that ensures we process filesync events\n * in the order we receive them.\n */\n private _syncOperations = new PQueue({ concurrency: 1 });\n\n constructor(readonly syncJson: SyncJson) {}\n\n async hashes(ctx: Context<SyncJsonArgs>): Promise<FileSyncHashes> {\n const spinner = spin({ ensureEmptyLineAbove: true })`\n Calculating file changes.\n `;\n\n try {\n const [localHashes, { localFilesVersionHashes, environmentHashes, environmentFilesVersion }] = await Promise.all([\n // get the hashes of our local files\n this.syncJson.directory.hashes(),\n // get the hashes of our local filesVersion and the latest filesVersion\n (async () => {\n let localFilesVersionHashes: Hashes;\n let environmentHashes: Hashes;\n let environmentFilesVersion: bigint;\n\n if (this.syncJson.filesVersion === 0n) {\n // we're either syncing for the first time or we're syncing a\n // non-empty directory without a `.gadget/sync.json` file,\n // regardless get the hashes of the latest filesVersion\n const { fileSyncHashes } = await this.syncJson.edit.query({ query: FILE_SYNC_HASHES_QUERY });\n environmentFilesVersion = BigInt(fileSyncHashes.filesVersion);\n environmentHashes = fileSyncHashes.hashes;\n localFilesVersionHashes = {}; // represents an empty directory\n } else {\n // this isn't the first time we're syncing, so get the\n // hashes of the files at our local filesVersion and the\n // latest filesVersion\n const { fileSyncComparisonHashes } = await this.syncJson.edit.query({\n query: FILE_SYNC_COMPARISON_HASHES_QUERY,\n variables: { filesVersion: String(this.syncJson.filesVersion) },\n });\n\n localFilesVersionHashes = fileSyncComparisonHashes.filesVersionHashes.hashes;\n environmentHashes = fileSyncComparisonHashes.latestFilesVersionHashes.hashes;\n environmentFilesVersion = BigInt(fileSyncComparisonHashes.latestFilesVersionHashes.filesVersion);\n }\n\n return { localFilesVersionHashes, environmentHashes, environmentFilesVersion };\n })(),\n ]);\n\n const inSync = isEqualHashes(ctx, localHashes, environmentHashes);\n\n const localChanges = getNecessaryChanges(ctx, {\n from: localFilesVersionHashes,\n to: localHashes,\n existing: environmentHashes,\n ignore: [\".gadget/\"], // gadget manages these files\n });\n\n let environmentChanges = getNecessaryChanges(ctx, {\n from: localFilesVersionHashes,\n to: environmentHashes,\n existing: localHashes,\n });\n\n if (!inSync && localChanges.size === 0 && environmentChanges.size === 0) {\n // we're not in sync, but neither the local filesystem nor the\n // environment's filesystem have any changes; this is only\n // possible if the local filesystem has modified .gadget/ files\n environmentChanges = getNecessaryChanges(ctx, { from: localHashes, to: environmentHashes });\n assert(environmentChanges.size > 0, \"expected environmentChanges to have changes\");\n assert(\n Array.from(environmentChanges.keys()).every((path) => path.startsWith(\".gadget/\")),\n \"expected all environmentChanges to be .gadget/ files\",\n );\n }\n\n assert(inSync || localChanges.size > 0 || environmentChanges.size > 0, \"there must be changes if hashes don't match\");\n\n const localChangesToPush = getNecessaryChanges(ctx, { from: environmentHashes, to: localHashes, ignore: [\".gadget/\"] });\n const environmentChangesToPull = getNecessaryChanges(ctx, { from: localHashes, to: environmentHashes });\n\n const onlyDotGadgetFilesChanged = Array.from(environmentChangesToPull.keys()).every((filepath) => filepath.startsWith(\".gadget/\"));\n const bothChanged = localChanges.size > 0 && environmentChanges.size > 0 && !onlyDotGadgetFilesChanged;\n\n if (inSync) {\n spinner.succeed`Your files are up to date. ${ts()}`;\n } else {\n spinner.succeed`Calculated file changes. ${ts()}`;\n }\n\n return {\n inSync,\n localFilesVersionHashes,\n localHashes,\n localChanges,\n localChangesToPush,\n environmentHashes,\n environmentChanges,\n environmentChangesToPull,\n environmentFilesVersion,\n onlyDotGadgetFilesChanged,\n bothChanged,\n };\n } catch (error) {\n spinner.fail();\n throw error;\n }\n }\n\n async print(ctx: Context<SyncJsonArgs>, { hashes }: { hashes?: FileSyncHashes } = {}): Promise<void> {\n const { inSync, localChanges, environmentChanges, onlyDotGadgetFilesChanged, bothChanged } = hashes ?? (await this.hashes(ctx));\n if (inSync) {\n // the spinner in hashes will have already printed that we're in sync\n return;\n }\n\n if (localChanges.size > 0) {\n printChanges(ctx, {\n changes: localChanges,\n tense: \"past\",\n title: sprint`Your local files {underline have} changed.`,\n });\n } else {\n println({ ensureEmptyLineAbove: true })`\n Your local files {underline have not} changed.\n `;\n }\n\n if (environmentChanges.size > 0 && !onlyDotGadgetFilesChanged) {\n printChanges(ctx, {\n changes: environmentChanges,\n tense: \"past\",\n title: sprint`Your environment's files {underline have}${bothChanged ? \" also\" : \"\"} changed.`,\n });\n } else {\n println({ ensureEmptyLineAbove: true })`\n Your environment's files {underline have not} changed.\n `;\n }\n }\n\n /**\n * Waits for all pending and ongoing filesync operations to complete.\n */\n async idle(): Promise<void> {\n await this._syncOperations.onIdle();\n }\n\n /**\n * Attempts to send file changes to the Gadget. If a files version\n * mismatch error occurs, this function will merge the changes with\n * Gadget instead.\n *\n * @param ctx - The context to use.\n * @param options - The options to use.\n * @param options.changes - The changes to send.\n * @param options.printLocalChangesOptions - The options to use when printing the local changes.\n * @param options.printEnvironmentChangesOptions - The options to use when printing the changes from Gadget.\n * @returns A promise that resolves when the changes have been sent.\n */\n async mergeChangesWithEnvironment(\n ctx: Context<DevArgs>,\n {\n changes,\n printLocalChangesOptions,\n printEnvironmentChangesOptions,\n }: {\n changes: Changes;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n await this._syncOperations.add(async () => {\n try {\n await this._sendChangesToEnvironment(ctx, { changes, printLocalChangesOptions });\n } catch (error) {\n swallowFilesVersionMismatch(ctx, error);\n // we either sent the wrong expectedFilesVersion or we received\n // a filesVersion that is greater than the expectedFilesVersion\n // + 1, so we need to stop what we're doing and get in sync\n await this.merge(ctx, { printEnvironmentChangesOptions });\n }\n });\n }\n\n /**\n * Subscribes to file changes on Gadget and executes the provided\n * callbacks before and after the changes occur.\n *\n * @param ctx - The context to use.\n * @param options - The options to use.\n * @param options.beforeChanges - A callback that is called before the changes occur.\n * @param options.afterChanges - A callback that is called after the changes occur.\n * @param options.onError - A callback that is called if an error occurs.\n * @param options.printEnvironmentChangesOptions - The options to use when printing the changes from Gadget.\n * @returns A function that unsubscribes from changes on Gadget.\n */\n subscribeToEnvironmentChanges(\n ctx: Context<DevArgs>,\n {\n beforeChanges = noop,\n printEnvironmentChangesOptions,\n afterChanges = noop,\n onError,\n }: {\n beforeChanges?: (data: { changed: string[]; deleted: string[] }) => Promisable<void>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n afterChanges?: (data: { changes: Changes }) => Promisable<void>;\n onError: (error: unknown) => void;\n },\n ): EditSubscription<REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION> {\n return this.syncJson.edit.subscribe({\n subscription: REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n // the reason this is a function rather than a static value is\n // so that it will be re-evaluated if the connection is lost and\n // then re-established. this ensures that we send our current\n // filesVersion rather than the one that was sent when we first\n // subscribed\n variables: () => ({ localFilesVersion: String(this.syncJson.filesVersion) }),\n onError,\n onData: ({ remoteFileSyncEvents: { changed, deleted, remoteFilesVersion } }) => {\n this._syncOperations\n .add(async () => {\n if (BigInt(remoteFilesVersion) < this.syncJson.filesVersion) {\n ctx.log.warn(\"skipping received changes because files version is outdated\", { filesVersion: remoteFilesVersion });\n return;\n }\n\n ctx.log.debug(\"received files\", {\n remoteFilesVersion,\n changed: changed.map((change) => change.path),\n deleted: deleted.map((change) => change.path),\n });\n\n const filterIgnoredFiles = (file: { path: string }): boolean => {\n const ignored = this.syncJson.directory.ignores(file.path);\n if (ignored) {\n ctx.log.warn(\"skipping received change because file is ignored\", { path: file.path });\n }\n return !ignored;\n };\n\n changed = changed.filter(filterIgnoredFiles);\n deleted = deleted.filter(filterIgnoredFiles);\n\n if (changed.length === 0 && deleted.length === 0) {\n await this.syncJson.save(remoteFilesVersion);\n return;\n }\n\n await beforeChanges({\n changed: changed.map((file) => file.path),\n deleted: deleted.map((file) => file.path),\n });\n\n const changes = await this._writeToLocalFilesystem(ctx, {\n filesVersion: remoteFilesVersion,\n files: changed,\n delete: deleted.map((file) => file.path),\n printEnvironmentChangesOptions: {\n tense: \"past\",\n ensureEmptyLineAbove: true,\n title: sprintln`{green ${symbol.tick}} Pulled ${pluralize(\"file\", changed.length + deleted.length)}. ${symbol.arrowLeft} ${ts()}`,\n limit: 5,\n ...printEnvironmentChangesOptions,\n },\n });\n\n await afterChanges({ changes });\n })\n .catch(onError);\n },\n });\n }\n\n /**\n * Ensures the local filesystem is in sync with Gadget's filesystem.\n * - All non-conflicting changes are automatically merged.\n * - Conflicts are resolved by prompting the user to either keep their local changes or keep Gadget's changes.\n * - This function will not return until the filesystem is in sync.\n */\n async merge(\n ctx: Context<DevArgs>,\n {\n hashes,\n maxAttempts = 10,\n printLocalChangesOptions,\n printEnvironmentChangesOptions,\n }: {\n hashes?: FileSyncHashes;\n maxAttempts?: number;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n } = {},\n ): Promise<void> {\n let attempt = 0;\n\n do {\n if (attempt === 0) {\n hashes ??= await this.hashes(ctx);\n } else {\n hashes = await this.hashes(ctx);\n }\n\n if (hashes.inSync) {\n this._syncOperations.clear();\n ctx.log.info(\"filesystem in sync\");\n await this.syncJson.save(hashes.environmentFilesVersion);\n return;\n }\n\n attempt += 1;\n ctx.log.info(\"merging\", { attempt, ...hashes });\n\n try {\n await this._merge(ctx, { hashes, printLocalChangesOptions, printEnvironmentChangesOptions });\n } catch (error) {\n swallowFilesVersionMismatch(ctx, error);\n // we either sent the wrong expectedFilesVersion or we received\n // a filesVersion that is greater than the expectedFilesVersion\n // + 1, so try again\n }\n } while (attempt < maxAttempts);\n\n throw new TooManyMergeAttemptsError(maxAttempts);\n }\n\n /**\n * Pushes any changes made to the local filesystem since the last sync\n * to Gadget.\n *\n * If Gadget has also made changes since the last sync, and --force\n * was not passed, the user will be prompted to discard them.\n */\n async push(\n ctx: Context<PullArgs>,\n {\n hashes,\n force,\n printLocalChangesOptions,\n }: {\n hashes?: FileSyncHashes;\n force?: boolean;\n printLocalChangesOptions?: PrintChangesOptions;\n } = {},\n ): Promise<void> {\n const { localChangesToPush, environmentChanges, environmentFilesVersion, onlyDotGadgetFilesChanged } =\n hashes ?? (await this.hashes(ctx));\n assert(localChangesToPush.size > 0, \"cannot push if there are no changes\");\n\n // TODO: lift this check up to the push command\n if (\n // they didn't pass --force\n !(force ?? ctx.args[\"--force\"]) &&\n // their environment's files have changed\n environmentChanges.size > 0 &&\n // some of the changes aren't .gadget/ files\n !onlyDotGadgetFilesChanged\n ) {\n await confirm({ ensureEmptyLineAbove: true })`\n Are you sure you want to {underline discard} your environment's changes?\n `;\n }\n\n try {\n await this._sendChangesToEnvironment(ctx, {\n // what changes need to be made to your local files to make\n // them match the environment's files\n changes: localChangesToPush,\n expectedFilesVersion: environmentFilesVersion,\n printLocalChangesOptions,\n });\n } catch (error) {\n swallowFilesVersionMismatch(ctx, error);\n // we were told to push their local changes, but their\n // environment's files have changed since we last checked, so\n // throw a nicer error message\n // TODO: we don't have to do this if only .gadget/ files changed\n throw new EdgeCaseError(sprint`\n Your environment's files have changed since we last checked.\n\n Please re-run \"ggt ${ctx.command}\" to see the changes and try again.\n `);\n }\n }\n\n async pull(\n ctx: Context<PullArgs>,\n {\n hashes,\n force,\n printEnvironmentChangesOptions,\n }: {\n hashes?: FileSyncHashes;\n force?: boolean;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n } = {},\n ): Promise<void> {\n const { localChanges, environmentChangesToPull, environmentFilesVersion } = hashes ?? (await this.hashes(ctx));\n assert(environmentChangesToPull.size > 0, \"cannot push if there are no changes\");\n\n // TODO: lift this check up to the pull command\n if (localChanges.size > 0 && !(force ?? ctx.args[\"--force\"])) {\n await confirm`\n Are you sure you want to {underline discard} your local changes?\n `;\n }\n\n await this._getChangesFromEnvironment(ctx, {\n changes: environmentChangesToPull,\n filesVersion: environmentFilesVersion,\n printEnvironmentChangesOptions,\n });\n }\n\n private async _merge(\n ctx: Context<DevArgs>,\n {\n hashes: { localChanges, environmentChanges, environmentFilesVersion },\n printLocalChangesOptions,\n printEnvironmentChangesOptions,\n }: {\n hashes: FileSyncHashes;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n const conflicts = getConflicts({ localChanges, environmentChanges });\n if (conflicts.size > 0) {\n ctx.log.debug(\"conflicts detected\", { conflicts });\n\n let preference = ctx.args[\"--prefer\"];\n if (!preference) {\n printConflicts({ conflicts });\n preference = await select({ choices: Object.values(MergeConflictPreference) })`\n {bold How should we resolve these conflicts?}\n `;\n }\n\n switch (preference) {\n case MergeConflictPreference.CANCEL: {\n process.exit(0);\n break;\n }\n case MergeConflictPreference.LOCAL: {\n environmentChanges = withoutConflictingChanges({ conflicts, changes: environmentChanges });\n break;\n }\n case MergeConflictPreference.ENVIRONMENT: {\n localChanges = withoutConflictingChanges({ conflicts, changes: localChanges });\n break;\n }\n }\n }\n\n if (environmentChanges.size > 0) {\n await this._getChangesFromEnvironment(ctx, {\n changes: environmentChanges,\n filesVersion: environmentFilesVersion,\n printEnvironmentChangesOptions,\n });\n }\n\n if (localChanges.size > 0) {\n await this._sendChangesToEnvironment(ctx, {\n changes: localChanges,\n expectedFilesVersion: environmentFilesVersion,\n printLocalChangesOptions,\n });\n }\n }\n\n private async _getChangesFromEnvironment(\n ctx: Context<SyncJsonArgs>,\n {\n filesVersion,\n changes,\n printEnvironmentChangesOptions,\n }: {\n filesVersion: bigint;\n changes: Changes | ChangesWithHash;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n ctx.log.debug(\"getting changes from gadget\", { filesVersion, changes });\n const created = changes.created();\n const updated = changes.updated();\n\n const spinner = spin({ ensureEmptyLineAbove: true })(\n sprintChanges(ctx, {\n changes,\n tense: \"present\",\n title: sprint`Pulling ${pluralize(\"file\", changes.size)}. ${symbol.arrowLeft}`,\n ...printEnvironmentChangesOptions,\n }),\n );\n\n try {\n let files: File[] = [];\n if (created.length > 0 || updated.length > 0) {\n const { fileSyncFiles } = await this.syncJson.edit.query({\n query: FILE_SYNC_FILES_QUERY,\n variables: {\n paths: [...created, ...updated],\n filesVersion: String(filesVersion),\n encoding: FileSyncEncoding.Base64,\n },\n });\n\n files = fileSyncFiles.files;\n }\n\n await this._writeToLocalFilesystem(ctx, {\n filesVersion,\n files,\n delete: changes.deleted(),\n spinner,\n printEnvironmentChangesOptions,\n });\n } catch (error) {\n spinner.fail();\n throw error;\n }\n }\n\n private async _sendChangesToEnvironment(\n ctx: Context<SyncJsonArgs>,\n {\n changes,\n expectedFilesVersion = this.syncJson.filesVersion,\n printLocalChangesOptions,\n }: {\n changes: Changes | ChangesWithHash;\n expectedFilesVersion?: bigint;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n ctx.log.debug(\"sending changes to gadget\", { expectedFilesVersion, changes });\n const changed: FileSyncChangedEventInput[] = [];\n const deleted: FileSyncDeletedEventInput[] = [];\n\n await pMap(changes, async ([normalizedPath, change]) => {\n if (change.type === \"delete\") {\n deleted.push({ path: normalizedPath });\n return;\n }\n\n const absolutePath = this.syncJson.directory.absolute(normalizedPath);\n\n let stats;\n try {\n stats = await fs.stat(absolutePath);\n } catch (error) {\n swallowEnoent(error);\n ctx.log.debug(\"skipping change because file doesn't exist\", { path: normalizedPath });\n return;\n }\n\n let content = \"\";\n if (stats.isFile()) {\n content = await fs.readFile(absolutePath, FileSyncEncoding.Base64);\n }\n\n let oldPath;\n if (change.type === \"create\" && change.oldPath) {\n oldPath = change.oldPath;\n }\n\n changed.push({\n content,\n oldPath,\n path: normalizedPath,\n mode: stats.mode,\n encoding: FileSyncEncoding.Base64,\n });\n });\n\n if (changed.length === 0 && deleted.length === 0) {\n ctx.log.debug(\"skipping send because there are no changes\");\n return;\n }\n\n const contentLength = changed.map((change) => change.content.length).reduce((a, b) => a + b, 0);\n if (contentLength > MAX_PUSH_CONTENT_LENGTH) {\n throw new EdgeCaseError(sprint`\n {underline Your file changes are too large to push.}\n\n Run \"ggt status\" to see your changes and consider\n ignoring some files or pushing in smaller batches.\n `);\n }\n\n const spinner = spin({ ensureEmptyLineAbove: true })(\n sprintChanges(ctx, {\n changes,\n tense: \"present\",\n title: sprintln`Pushing ${pluralize(\"file\", changed.length + deleted.length)}. ${symbol.arrowRight}`,\n ...printLocalChangesOptions,\n }),\n );\n\n try {\n const {\n publishFileSyncEvents: { remoteFilesVersion, problems: filesyncProblems },\n } = await this.syncJson.edit.mutate({\n mutation: PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n variables: {\n input: {\n expectedRemoteFilesVersion: String(expectedFilesVersion),\n changed,\n deleted,\n },\n },\n http: {\n retry: {\n // we can retry this request because\n // expectedRemoteFilesVersion makes it idempotent\n methods: [\"POST\"],\n calculateDelay: ({ error, computedValue }) => {\n if (isFilesVersionMismatchError(error.response?.body)) {\n // don't retry if we get a files version mismatch error\n return 0;\n }\n return computedValue;\n },\n },\n },\n });\n\n if (BigInt(remoteFilesVersion) > expectedFilesVersion + 1n) {\n // we can't save the remoteFilesVersion because we haven't\n // received the intermediate filesVersions yet\n throw new Error(\"Files version mismatch\");\n }\n\n await this.syncJson.save(remoteFilesVersion);\n\n spinner.succeed(\n sprintChanges(ctx, {\n changes,\n tense: \"past\",\n title: sprintln`Pushed ${pluralize(\"file\", changed.length + deleted.length)}. ${symbol.arrowRight} ${ts()}`,\n ...printLocalChangesOptions,\n }),\n );\n\n if (filesyncProblems.length > 0) {\n println({ ensureEmptyLineAbove: true })`\n {red Gadget has detected the following fatal errors with your files:}\n\n ${sprintProblems({\n problems: filesyncProblemsToProblems(filesyncProblems),\n showFileTypes: false,\n indent: 10,\n })}\n\n {red Your app will not be operational until all fatal errors are fixed.}\n `;\n }\n } catch (error) {\n if (isFilesVersionMismatchError(error)) {\n spinner.clear();\n } else {\n spinner.fail();\n }\n\n throw error;\n }\n }\n\n private async _writeToLocalFilesystem(\n ctx: Context<SyncJsonArgs>,\n options: {\n filesVersion: bigint | string;\n files: File[];\n delete: string[];\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n spinner?: spinner;\n },\n ): Promise<Changes> {\n const filesVersion = BigInt(options.filesVersion);\n assert(filesVersion >= this.syncJson.filesVersion, \"filesVersion must be greater than or equal to current filesVersion\");\n\n ctx.log.debug(\"writing to local filesystem\", {\n filesVersion,\n files: options.files.map((file) => file.path),\n delete: options.delete,\n });\n\n const changes = new Changes();\n const directoriesWithDeletedFiles = new Set<string>();\n\n await pMap(options.delete, async (pathToDelete) => {\n // add all the directories that contain this file to\n // directoriesWithDeletedFiles so we can clean them up later\n let dir = path.dirname(pathToDelete);\n while (dir !== \".\") {\n directoriesWithDeletedFiles.add(this.syncJson.directory.normalize(dir, true));\n dir = path.dirname(dir);\n }\n\n const currentPath = this.syncJson.directory.absolute(pathToDelete);\n const backupPath = this.syncJson.directory.absolute(\".gadget/backup\", this.syncJson.directory.relative(pathToDelete));\n\n // rather than `rm -rf`ing files, we move them to\n // `.gadget/backup/` so that users can recover them if something\n // goes wrong. We've seen a lot of EBUSY/EINVAL errors when moving\n // files so we retry a few times.\n await pRetry(\n async () => {\n try {\n // remove the current backup file in case it exists and is a\n // different type (file vs directory)\n await fs.remove(backupPath);\n await fs.move(currentPath, backupPath);\n changes.set(pathToDelete, { type: \"delete\" });\n } catch (error) {\n if (isENOENTError(error)) {\n // replicate the behavior of `rm -rf` and ignore ENOENT\n return;\n }\n\n if (isENOTDIRError(error) || isEEXISTError(error)) {\n // the backup path already exists and ends in a file\n // rather than a directory, so we have to remove the file\n // before we can move the current path to the backup path\n let dir = path.dirname(backupPath);\n while (dir !== this.syncJson.directory.absolute(\".gadget/backup\")) {\n const stats = await fs.stat(dir);\n // eslint-disable-next-line max-depth\n if (!stats.isDirectory()) {\n // this file is in the way, so remove it\n ctx.log.debug(\"removing file in the way of backup path\", { currentPath, backupPath, file: dir });\n await fs.remove(dir);\n }\n dir = path.dirname(dir);\n }\n // still throw the error so we retry\n }\n\n throw error;\n }\n },\n {\n // windows tends to run into these issues way more often than\n // mac/linux, so we retry more times\n retries: config.windows ? 4 : 2,\n minTimeout: ms(\"100ms\"),\n onFailedAttempt: (error) => {\n ctx.log.warn(\"failed to move file to backup\", { error, currentPath, backupPath });\n },\n },\n );\n });\n\n for (const directoryWithDeletedFile of Array.from(directoriesWithDeletedFiles.values()).sort().reverse()) {\n if (options.files.some((file) => file.path === directoryWithDeletedFile)) {\n // we're about to create this directory, so we don't need to\n // clean it up\n continue;\n }\n\n try {\n // delete any empty directories that contained a deleted file.\n // if the empty directory should continue to exist, we would\n // have received an event to create it above\n await fs.rmdir(this.syncJson.directory.absolute(directoryWithDeletedFile));\n changes.set(directoryWithDeletedFile, { type: \"delete\" });\n } catch (error) {\n if (isENOENTError(error) || isENOTEMPTYError(error)) {\n // noop if the directory doesn't exist or isn't empty\n continue;\n }\n throw error;\n }\n }\n\n await pMap(options.files, async (file) => {\n const absolutePath = this.syncJson.directory.absolute(file.path);\n if (await fs.pathExists(absolutePath)) {\n if (!file.path.endsWith(\"/\")) {\n // only track file updates, not directory updates\n changes.set(file.path, { type: \"update\" });\n }\n } else {\n changes.set(file.path, { type: \"create\" });\n }\n\n if (file.path.endsWith(\"/\")) {\n await fs.ensureDir(absolutePath);\n } else {\n await fs.outputFile(absolutePath, Buffer.from(file.content, file.encoding));\n }\n\n if (supportsPermissions) {\n // the os's default umask makes setting the mode during creation\n // not work, so an additional fs.chmod call is necessary to\n // ensure the file has the correct mode\n await fs.chmod(absolutePath, file.mode & 0o777);\n }\n\n if (absolutePath === this.syncJson.directory.absolute(\".ignore\")) {\n await this.syncJson.directory.loadIgnoreFile();\n }\n });\n\n await this.syncJson.save(String(filesVersion));\n\n options.spinner?.clear();\n\n printChanges(ctx, {\n changes,\n tense: \"past\",\n title: sprint`{green ${symbol.tick}} Pulled ${pluralize(\"file\", changes.size)}. ${symbol.arrowLeft} ${ts()}`,\n ...options.printEnvironmentChangesOptions,\n });\n\n if (changes.has(\"yarn.lock\")) {\n const spinner = spin({ ensureEmptyLineAbove: true })('Running \"yarn install --check-files\"');\n\n try {\n await execa(\"yarn\", [\"install\", \"--check-files\"], { cwd: this.syncJson.directory.path });\n spinner.succeed`Ran \"yarn install --check-files\" ${ts()}`;\n } catch (error) {\n spinner.fail();\n ctx.log.error(\"yarn install failed\", { error });\n\n const message = serializeError(error).message;\n if (message) {\n println({ ensureEmptyLineAbove: true, indent: 2 })(message);\n }\n }\n }\n\n return changes;\n }\n}\n"],"names":["execa","fs","ms","assert","path","process","pMap","PQueue","pRetry","pluralize","FileSyncEncoding","FILE_SYNC_COMPARISON_HASHES_QUERY","FILE_SYNC_FILES_QUERY","FILE_SYNC_HASHES_QUERY","PUBLISH_FILE_SYNC_EVENTS_MUTATION","REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION","config","confirm","println","filesyncProblemsToProblems","sprintProblems","EdgeCaseError","select","spin","sprint","sprintln","symbol","ts","noop","isEEXISTError","isENOENTError","isENOTDIRError","isENOTEMPTYError","serializeError","Changes","printChanges","sprintChanges","getConflicts","printConflicts","withoutConflictingChanges","supportsPermissions","swallowEnoent","TooManyMergeAttemptsError","isFilesVersionMismatchError","swallowFilesVersionMismatch","getNecessaryChanges","isEqualHashes","MergeConflictPreference","MAX_MERGE_ATTEMPTS","MAX_PUSH_CONTENT_LENGTH","FileSync","hashes","ctx","spinner","ensureEmptyLineAbove","localHashes","localFilesVersionHashes","environmentHashes","environmentFilesVersion","Promise","all","syncJson","directory","filesVersion","fileSyncHashes","edit","query","BigInt","fileSyncComparisonHashes","variables","String","filesVersionHashes","latestFilesVersionHashes","inSync","localChanges","from","to","existing","ignore","environmentChanges","size","Array","keys","every","startsWith","localChangesToPush","environmentChangesToPull","onlyDotGadgetFilesChanged","filepath","bothChanged","succeed","error","fail","print","changes","tense","title","idle","_syncOperations","onIdle","mergeChangesWithEnvironment","printLocalChangesOptions","printEnvironmentChangesOptions","add","_sendChangesToEnvironment","merge","subscribeToEnvironmentChanges","beforeChanges","afterChanges","onError","subscribe","subscription","localFilesVersion","onData","remoteFileSyncEvents","changed","deleted","remoteFilesVersion","log","warn","debug","map","change","filterIgnoredFiles","file","ignored","ignores","filter","length","save","_writeToLocalFilesystem","files","delete","tick","arrowLeft","limit","catch","maxAttempts","attempt","clear","info","_merge","push","force","args","expectedFilesVersion","command","pull","_getChangesFromEnvironment","conflicts","preference","choices","Object","values","CANCEL","exit","LOCAL","ENVIRONMENT","created","updated","fileSyncFiles","paths","encoding","Base64","normalizedPath","type","absolutePath","absolute","stats","stat","content","isFile","readFile","oldPath","mode","contentLength","reduce","a","b","arrowRight","publishFileSyncEvents","problems","filesyncProblems","mutate","mutation","input","expectedRemoteFilesVersion","http","retry","methods","calculateDelay","computedValue","response","body","Error","showFileTypes","indent","options","directoriesWithDeletedFiles","Set","pathToDelete","dir","dirname","normalize","currentPath","backupPath","relative","remove","move","set","isDirectory","retries","windows","minTimeout","onFailedAttempt","directoryWithDeletedFile","sort","reverse","some","rmdir","pathExists","endsWith","ensureDir","outputFile","Buffer","chmod","loadIgnoreFile","has","cwd","message","constructor","concurrency"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/services/filesync/filesync.ts"],"sourcesContent":["import { execa } from \"execa\";\nimport fs from \"fs-extra\";\nimport ms from \"ms\";\nimport assert from \"node:assert\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport pMap from \"p-map\";\nimport PQueue from \"p-queue\";\nimport pRetry from \"p-retry\";\nimport pluralize from \"pluralize\";\nimport type { Promisable } from \"type-fest\";\nimport { FileSyncEncoding, type FileSyncChangedEventInput, type FileSyncDeletedEventInput } from \"../../__generated__/graphql.js\";\nimport type { DevArgs } from \"../../commands/dev.js\";\nimport type { PullArgs } from \"../../commands/pull.js\";\nimport { type EditSubscription } from \"../app/edit/edit.js\";\nimport {\n FILE_SYNC_COMPARISON_HASHES_QUERY,\n FILE_SYNC_FILES_QUERY,\n FILE_SYNC_HASHES_QUERY,\n PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n} from \"../app/edit/operation.js\";\nimport type { Context } from \"../command/context.js\";\nimport { config } from \"../config/config.js\";\nimport { confirm } from \"../output/confirm.js\";\nimport { println } from \"../output/print.js\";\nimport { filesyncProblemsToProblems, sprintProblems } from \"../output/problems.js\";\nimport { EdgeCaseError } from \"../output/report.js\";\nimport { select } from \"../output/select.js\";\nimport { spin, type spinner } from \"../output/spinner.js\";\nimport { sprint, sprintln } from \"../output/sprint.js\";\nimport { symbol } from \"../output/symbols.js\";\nimport { ts } from \"../output/timestamp.js\";\nimport { noop } from \"../util/function.js\";\nimport { isEEXISTError, isENOENTError, isENOTDIRError, isENOTEMPTYError } from \"../util/is.js\";\nimport { serializeError } from \"../util/object.js\";\nimport { Changes, printChanges, sprintChanges, type PrintChangesOptions } from \"./changes.js\";\nimport { getConflicts, printConflicts, withoutConflictingChanges } from \"./conflicts.js\";\nimport { supportsPermissions, swallowEnoent, type Hashes } from \"./directory.js\";\nimport { TooManyMergeAttemptsError, isFilesVersionMismatchError, swallowFilesVersionMismatch } from \"./error.js\";\nimport type { File } from \"./file.js\";\nimport { getNecessaryChanges, isEqualHashes, type ChangesWithHash } from \"./hashes.js\";\nimport { MergeConflictPreference } from \"./strategy.js\";\nimport { type SyncJson, type SyncJsonArgs } from \"./sync-json.js\";\n\n/**\n * The maximum attempts to automatically merge local and environment\n * file changes when a FilesVersionMismatchError is encountered before\n * throwing a {@linkcode TooManyMergeAttemptsError}.\n */\nexport const MAX_MERGE_ATTEMPTS = 10;\n\n/**\n * The maximum length of file content that can be pushed to Gadget in a\n * single request.\n */\nexport const MAX_PUSH_CONTENT_LENGTH = 50 * 1024 * 1024; // 50mb\n\nexport type FileSyncHashes = {\n /**\n * Whether the local filesystem is in sync with the environment's\n * filesystem.\n */\n inSync: boolean;\n\n /**\n * Whether the local filesystem and the environment's filesystem have\n * both changed since the last sync.\n */\n bothChanged: boolean;\n\n /**\n * Whether only .gadget/ files have changed on the environment's\n * filesystem.\n */\n onlyDotGadgetFilesChanged: boolean;\n\n /**\n * The hashes of the files at the local filesVersion.\n */\n localFilesVersionHashes: Hashes;\n\n /**\n * The hashes of the files on the local filesystem.\n */\n localHashes: Hashes;\n\n /**\n * The changes the local filesystem has made since the last sync.\n */\n localChanges: ChangesWithHash;\n\n /**\n * The changes the local filesystem needs to push to make the\n * environment's filesystem in sync with the local filesystem.\n *\n * NOTE: If the environment's filesystem has changed since the last\n * sync, these changes will undo those changes.\n */\n localChangesToPush: Changes | ChangesWithHash;\n\n /**\n * The filesVersion of the environment's filesystem.\n */\n environmentFilesVersion: bigint;\n\n /**\n * The hashes of the files on the environment's filesystem.\n */\n environmentHashes: Hashes;\n\n /**\n * The changes the environment's filesystem has made since the last\n * sync.\n */\n environmentChanges: ChangesWithHash;\n\n /**\n * The changes the local filesystem needs to pull from the\n * environment's filesystem to be in sync with the environment's\n * filesystem.\n *\n * NOTE: If the local filesystem has changed since the last sync,\n * these changes will undo those changes.\n */\n environmentChangesToPull: Changes | ChangesWithHash;\n};\n\nexport class FileSync {\n /**\n * A FIFO async callback queue that ensures we process filesync events\n * in the order we receive them.\n */\n private _syncOperations = new PQueue({ concurrency: 1 });\n\n constructor(readonly syncJson: SyncJson) {}\n\n async hashes(ctx: Context<SyncJsonArgs>, quietly?: boolean): Promise<FileSyncHashes> {\n const spinner = !quietly\n ? spin({ ensureEmptyLineAbove: true })`\n Calculating file changes.\n `\n : undefined;\n\n try {\n const [localHashes, { localFilesVersionHashes, environmentHashes, environmentFilesVersion }] = await Promise.all([\n // get the hashes of our local files\n this.syncJson.directory.hashes(),\n // get the hashes of our local filesVersion and the latest filesVersion\n (async () => {\n let localFilesVersionHashes: Hashes;\n let environmentHashes: Hashes;\n let environmentFilesVersion: bigint;\n\n if (this.syncJson.filesVersion === 0n) {\n // we're either syncing for the first time or we're syncing a\n // non-empty directory without a `.gadget/sync.json` file,\n // regardless get the hashes of the latest filesVersion\n const { fileSyncHashes } = await this.syncJson.edit.query({ query: FILE_SYNC_HASHES_QUERY });\n environmentFilesVersion = BigInt(fileSyncHashes.filesVersion);\n environmentHashes = fileSyncHashes.hashes;\n localFilesVersionHashes = {}; // represents an empty directory\n } else {\n // this isn't the first time we're syncing, so get the\n // hashes of the files at our local filesVersion and the\n // latest filesVersion\n const { fileSyncComparisonHashes } = await this.syncJson.edit.query({\n query: FILE_SYNC_COMPARISON_HASHES_QUERY,\n variables: { filesVersion: String(this.syncJson.filesVersion) },\n });\n\n localFilesVersionHashes = fileSyncComparisonHashes.filesVersionHashes.hashes;\n environmentHashes = fileSyncComparisonHashes.latestFilesVersionHashes.hashes;\n environmentFilesVersion = BigInt(fileSyncComparisonHashes.latestFilesVersionHashes.filesVersion);\n }\n\n return { localFilesVersionHashes, environmentHashes, environmentFilesVersion };\n })(),\n ]);\n\n const inSync = isEqualHashes(ctx, localHashes, environmentHashes);\n\n const localChanges = getNecessaryChanges(ctx, {\n from: localFilesVersionHashes,\n to: localHashes,\n existing: environmentHashes,\n ignore: [\".gadget/\"], // gadget manages these files\n });\n\n let environmentChanges = getNecessaryChanges(ctx, {\n from: localFilesVersionHashes,\n to: environmentHashes,\n existing: localHashes,\n });\n\n if (!inSync && localChanges.size === 0 && environmentChanges.size === 0) {\n // we're not in sync, but neither the local filesystem nor the\n // environment's filesystem have any changes; this is only\n // possible if the local filesystem has modified .gadget/ files\n environmentChanges = getNecessaryChanges(ctx, { from: localHashes, to: environmentHashes });\n assert(environmentChanges.size > 0, \"expected environmentChanges to have changes\");\n assert(\n Array.from(environmentChanges.keys()).every((path) => path.startsWith(\".gadget/\")),\n \"expected all environmentChanges to be .gadget/ files\",\n );\n }\n\n assert(inSync || localChanges.size > 0 || environmentChanges.size > 0, \"there must be changes if hashes don't match\");\n\n const localChangesToPush = getNecessaryChanges(ctx, { from: environmentHashes, to: localHashes, ignore: [\".gadget/\"] });\n const environmentChangesToPull = getNecessaryChanges(ctx, { from: localHashes, to: environmentHashes });\n\n const onlyDotGadgetFilesChanged = Array.from(environmentChangesToPull.keys()).every((filepath) => filepath.startsWith(\".gadget/\"));\n const bothChanged = localChanges.size > 0 && environmentChanges.size > 0 && !onlyDotGadgetFilesChanged;\n\n if (spinner) {\n if (inSync) {\n spinner.succeed`Your files are up to date. ${ts()}`;\n } else {\n spinner.succeed`Calculated file changes. ${ts()}`;\n }\n }\n\n return {\n inSync,\n localFilesVersionHashes,\n localHashes,\n localChanges,\n localChangesToPush,\n environmentHashes,\n environmentChanges,\n environmentChangesToPull,\n environmentFilesVersion,\n onlyDotGadgetFilesChanged,\n bothChanged,\n };\n } catch (error) {\n if (spinner) {\n spinner.fail();\n }\n throw error;\n }\n }\n\n async print(ctx: Context<SyncJsonArgs>, { hashes }: { hashes?: FileSyncHashes } = {}): Promise<void> {\n const { inSync, localChanges, environmentChanges, onlyDotGadgetFilesChanged, bothChanged } = hashes ?? (await this.hashes(ctx));\n if (inSync) {\n // the spinner in hashes will have already printed that we're in sync\n return;\n }\n\n if (localChanges.size > 0) {\n printChanges(ctx, {\n changes: localChanges,\n tense: \"past\",\n title: sprint`Your local files {underline have} changed.`,\n });\n } else {\n println({ ensureEmptyLineAbove: true })`\n Your local files {underline have not} changed.\n `;\n }\n\n if (environmentChanges.size > 0 && !onlyDotGadgetFilesChanged) {\n printChanges(ctx, {\n changes: environmentChanges,\n tense: \"past\",\n title: sprint`Your environment's files {underline have}${bothChanged ? \" also\" : \"\"} changed.`,\n });\n } else {\n println({ ensureEmptyLineAbove: true })`\n Your environment's files {underline have not} changed.\n `;\n }\n }\n\n /**\n * Waits for all pending and ongoing filesync operations to complete.\n */\n async idle(): Promise<void> {\n await this._syncOperations.onIdle();\n }\n\n /**\n * Attempts to send file changes to the Gadget. If a files version\n * mismatch error occurs, this function will merge the changes with\n * Gadget instead.\n *\n * @param ctx - The context to use.\n * @param options - The options to use.\n * @param options.changes - The changes to send.\n * @param options.printLocalChangesOptions - The options to use when printing the local changes.\n * @param options.printEnvironmentChangesOptions - The options to use when printing the changes from Gadget.\n * @returns A promise that resolves when the changes have been sent.\n */\n async mergeChangesWithEnvironment(\n ctx: Context<DevArgs>,\n {\n changes,\n printLocalChangesOptions,\n printEnvironmentChangesOptions,\n }: {\n changes: Changes;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n await this._syncOperations.add(async () => {\n try {\n await this._sendChangesToEnvironment(ctx, { changes, printLocalChangesOptions });\n } catch (error) {\n swallowFilesVersionMismatch(ctx, error);\n // we either sent the wrong expectedFilesVersion or we received\n // a filesVersion that is greater than the expectedFilesVersion\n // + 1, so we need to stop what we're doing and get in sync\n await this.merge(ctx, { printEnvironmentChangesOptions });\n }\n });\n }\n\n /**\n * Subscribes to file changes on Gadget and executes the provided\n * callbacks before and after the changes occur.\n *\n * @param ctx - The context to use.\n * @param options - The options to use.\n * @param options.beforeChanges - A callback that is called before the changes occur.\n * @param options.afterChanges - A callback that is called after the changes occur.\n * @param options.onError - A callback that is called if an error occurs.\n * @param options.printEnvironmentChangesOptions - The options to use when printing the changes from Gadget.\n * @returns A function that unsubscribes from changes on Gadget.\n */\n subscribeToEnvironmentChanges(\n ctx: Context<DevArgs>,\n {\n beforeChanges = noop,\n printEnvironmentChangesOptions,\n afterChanges = noop,\n onError,\n }: {\n beforeChanges?: (data: { changed: string[]; deleted: string[] }) => Promisable<void>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n afterChanges?: (data: { changes: Changes }) => Promisable<void>;\n onError: (error: unknown) => void;\n },\n ): EditSubscription<REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION> {\n return this.syncJson.edit.subscribe({\n subscription: REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n // the reason this is a function rather than a static value is\n // so that it will be re-evaluated if the connection is lost and\n // then re-established. this ensures that we send our current\n // filesVersion rather than the one that was sent when we first\n // subscribed\n variables: () => ({ localFilesVersion: String(this.syncJson.filesVersion) }),\n onError,\n onData: ({ remoteFileSyncEvents: { changed, deleted, remoteFilesVersion } }) => {\n this._syncOperations\n .add(async () => {\n if (BigInt(remoteFilesVersion) < this.syncJson.filesVersion) {\n ctx.log.warn(\"skipping received changes because files version is outdated\", { filesVersion: remoteFilesVersion });\n return;\n }\n\n ctx.log.debug(\"received files\", {\n remoteFilesVersion,\n changed: changed.map((change) => change.path),\n deleted: deleted.map((change) => change.path),\n });\n\n const filterIgnoredFiles = (file: { path: string }): boolean => {\n const ignored = this.syncJson.directory.ignores(file.path);\n if (ignored) {\n ctx.log.warn(\"skipping received change because file is ignored\", { path: file.path });\n }\n return !ignored;\n };\n\n changed = changed.filter(filterIgnoredFiles);\n deleted = deleted.filter(filterIgnoredFiles);\n\n if (changed.length === 0 && deleted.length === 0) {\n await this.syncJson.save(remoteFilesVersion);\n return;\n }\n\n await beforeChanges({\n changed: changed.map((file) => file.path),\n deleted: deleted.map((file) => file.path),\n });\n\n const changes = await this.writeToLocalFilesystem(ctx, {\n filesVersion: remoteFilesVersion,\n files: changed,\n delete: deleted.map((file) => file.path),\n printEnvironmentChangesOptions: {\n tense: \"past\",\n ensureEmptyLineAbove: true,\n title: sprintln`{greenBright ${symbol.tick}} Pulled ${pluralize(\"file\", changed.length + deleted.length)}. ${ts()}`,\n limit: 5,\n ...printEnvironmentChangesOptions,\n },\n });\n\n await afterChanges({ changes });\n })\n .catch(onError);\n },\n });\n }\n\n /**\n * Ensures the local filesystem is in sync with Gadget's filesystem.\n * - All non-conflicting changes are automatically merged.\n * - Conflicts are resolved by prompting the user to either keep their local changes or keep Gadget's changes.\n * - This function will not return until the filesystem is in sync.\n */\n async merge(\n ctx: Context<DevArgs>,\n {\n hashes,\n maxAttempts = 10,\n printLocalChangesOptions,\n printEnvironmentChangesOptions,\n quietly,\n }: {\n hashes?: FileSyncHashes;\n maxAttempts?: number;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n quietly?: boolean | undefined;\n } = {},\n ): Promise<void> {\n let attempt = 0;\n\n do {\n if (attempt === 0) {\n hashes ??= await this.hashes(ctx, quietly);\n } else {\n hashes = await this.hashes(ctx, quietly);\n }\n\n if (hashes.inSync) {\n this._syncOperations.clear();\n ctx.log.info(\"filesystem in sync\");\n await this.syncJson.save(hashes.environmentFilesVersion);\n return;\n }\n\n attempt += 1;\n ctx.log.info(\"merging\", { attempt, ...hashes });\n\n try {\n await this._merge(ctx, { hashes, printLocalChangesOptions, printEnvironmentChangesOptions });\n } catch (error) {\n swallowFilesVersionMismatch(ctx, error);\n // we either sent the wrong expectedFilesVersion or we received\n // a filesVersion that is greater than the expectedFilesVersion\n // + 1, so try again\n }\n } while (attempt < maxAttempts);\n\n throw new TooManyMergeAttemptsError(maxAttempts);\n }\n\n /**\n * Pushes any changes made to the local filesystem since the last sync\n * to Gadget.\n *\n * If Gadget has also made changes since the last sync, and --force\n * was not passed, the user will be prompted to discard them.\n */\n async push(\n ctx: Context<PullArgs>,\n {\n hashes,\n force,\n printLocalChangesOptions,\n }: {\n hashes?: FileSyncHashes;\n force?: boolean;\n printLocalChangesOptions?: PrintChangesOptions;\n } = {},\n ): Promise<void> {\n const { localChangesToPush, environmentChanges, environmentFilesVersion, onlyDotGadgetFilesChanged } =\n hashes ?? (await this.hashes(ctx));\n assert(localChangesToPush.size > 0, \"cannot push if there are no changes\");\n\n // TODO: lift this check up to the push command\n if (\n // they didn't pass --force\n !(force ?? ctx.args[\"--force\"]) &&\n // their environment's files have changed\n environmentChanges.size > 0 &&\n // some of the changes aren't .gadget/ files\n !onlyDotGadgetFilesChanged\n ) {\n await confirm({ ensureEmptyLineAbove: true })`\n Are you sure you want to {underline discard} your environment's changes?\n `;\n }\n\n try {\n await this._sendChangesToEnvironment(ctx, {\n // what changes need to be made to your local files to make\n // them match the environment's files\n changes: localChangesToPush,\n expectedFilesVersion: environmentFilesVersion,\n printLocalChangesOptions,\n });\n } catch (error) {\n swallowFilesVersionMismatch(ctx, error);\n // we were told to push their local changes, but their\n // environment's files have changed since we last checked, so\n // throw a nicer error message\n // TODO: we don't have to do this if only .gadget/ files changed\n throw new EdgeCaseError(sprint`\n Your environment's files have changed since we last checked.\n\n Please re-run \"ggt ${ctx.command}\" to see the changes and try again.\n `);\n }\n }\n\n async pull(\n ctx: Context<PullArgs>,\n {\n hashes,\n force,\n printEnvironmentChangesOptions,\n }: {\n hashes?: FileSyncHashes;\n force?: boolean;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n } = {},\n ): Promise<void> {\n const { localChanges, environmentChangesToPull, environmentFilesVersion } = hashes ?? (await this.hashes(ctx));\n assert(environmentChangesToPull.size > 0, \"cannot push if there are no changes\");\n\n // TODO: lift this check up to the pull command\n if (localChanges.size > 0 && !(force ?? ctx.args[\"--force\"])) {\n await confirm`\n Are you sure you want to {underline discard} your local changes?\n `;\n }\n\n await this._getChangesFromEnvironment(ctx, {\n changes: environmentChangesToPull,\n filesVersion: environmentFilesVersion,\n printEnvironmentChangesOptions,\n });\n }\n\n async writeToLocalFilesystem(\n ctx: Context<SyncJsonArgs>,\n options: {\n filesVersion: bigint | string;\n files: File[];\n delete: string[];\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n spinner?: spinner;\n },\n ): Promise<Changes> {\n const filesVersion = BigInt(options.filesVersion);\n assert(filesVersion >= this.syncJson.filesVersion, \"filesVersion must be greater than or equal to current filesVersion\");\n\n ctx.log.debug(\"writing to local filesystem\", {\n filesVersion,\n files: options.files.map((file) => file.path),\n delete: options.delete,\n });\n\n const changes = new Changes();\n const directoriesWithDeletedFiles = new Set<string>();\n\n await pMap(options.delete, async (pathToDelete) => {\n // add all the directories that contain this file to\n // directoriesWithDeletedFiles so we can clean them up later\n let dir = path.dirname(pathToDelete);\n while (dir !== \".\") {\n directoriesWithDeletedFiles.add(this.syncJson.directory.normalize(dir, true));\n dir = path.dirname(dir);\n }\n\n const currentPath = this.syncJson.directory.absolute(pathToDelete);\n const backupPath = this.syncJson.directory.absolute(\".gadget/backup\", this.syncJson.directory.relative(pathToDelete));\n\n // rather than `rm -rf`ing files, we move them to\n // `.gadget/backup/` so that users can recover them if something\n // goes wrong. We've seen a lot of EBUSY/EINVAL errors when moving\n // files so we retry a few times.\n await pRetry(\n async () => {\n try {\n // remove the current backup file in case it exists and is a\n // different type (file vs directory)\n await fs.remove(backupPath);\n await fs.move(currentPath, backupPath);\n changes.set(pathToDelete, { type: \"delete\" });\n } catch (error) {\n if (isENOENTError(error)) {\n // replicate the behavior of `rm -rf` and ignore ENOENT\n return;\n }\n\n if (isENOTDIRError(error) || isEEXISTError(error)) {\n // the backup path already exists and ends in a file\n // rather than a directory, so we have to remove the file\n // before we can move the current path to the backup path\n let dir = path.dirname(backupPath);\n while (dir !== this.syncJson.directory.absolute(\".gadget/backup\")) {\n const stats = await fs.stat(dir);\n // eslint-disable-next-line max-depth\n if (!stats.isDirectory()) {\n // this file is in the way, so remove it\n ctx.log.debug(\"removing file in the way of backup path\", { currentPath, backupPath, file: dir });\n await fs.remove(dir);\n }\n dir = path.dirname(dir);\n }\n // still throw the error so we retry\n }\n\n throw error;\n }\n },\n {\n // windows tends to run into these issues way more often than\n // mac/linux, so we retry more times\n retries: config.windows ? 4 : 2,\n minTimeout: ms(\"100ms\"),\n onFailedAttempt: (error) => {\n ctx.log.warn(\"failed to move file to backup\", { error, currentPath, backupPath });\n },\n },\n );\n });\n\n for (const directoryWithDeletedFile of Array.from(directoriesWithDeletedFiles.values()).sort().reverse()) {\n if (options.files.some((file) => file.path === directoryWithDeletedFile)) {\n // we're about to create this directory, so we don't need to\n // clean it up\n continue;\n }\n\n try {\n // delete any empty directories that contained a deleted file.\n // if the empty directory should continue to exist, we would\n // have received an event to create it above\n await fs.rmdir(this.syncJson.directory.absolute(directoryWithDeletedFile));\n changes.set(directoryWithDeletedFile, { type: \"delete\" });\n } catch (error) {\n if (isENOENTError(error) || isENOTEMPTYError(error)) {\n // noop if the directory doesn't exist or isn't empty\n continue;\n }\n throw error;\n }\n }\n\n await pMap(options.files, async (file) => {\n const absolutePath = this.syncJson.directory.absolute(file.path);\n if (await fs.pathExists(absolutePath)) {\n if (!file.path.endsWith(\"/\")) {\n // only track file updates, not directory updates\n changes.set(file.path, { type: \"update\" });\n }\n } else {\n changes.set(file.path, { type: \"create\" });\n }\n\n if (file.path.endsWith(\"/\")) {\n await fs.ensureDir(absolutePath);\n } else {\n await fs.outputFile(absolutePath, Buffer.from(file.content, file.encoding));\n }\n\n if (supportsPermissions) {\n // the os's default umask makes setting the mode during creation\n // not work, so an additional fs.chmod call is necessary to\n // ensure the file has the correct mode\n await fs.chmod(absolutePath, file.mode & 0o777);\n }\n\n if (absolutePath === this.syncJson.directory.absolute(\".ignore\")) {\n await this.syncJson.directory.loadIgnoreFile();\n }\n });\n\n await this.syncJson.save(String(filesVersion));\n\n options.spinner?.clear();\n\n printChanges(ctx, {\n changes,\n tense: \"past\",\n title: sprint`{greenBright ${symbol.arrowDown}} Pulled ${pluralize(\"file\", changes.size)}. ${ts()}`,\n ...options.printEnvironmentChangesOptions,\n includeDotGadget: !!ctx.args[\"--verbose\"],\n });\n\n if (changes.has(\"yarn.lock\")) {\n const spinner = spin({ ensureEmptyLineAbove: true })('Running \"yarn install --check-files\"');\n\n try {\n await execa(\"yarn\", [\"install\", \"--check-files\"], { cwd: this.syncJson.directory.path });\n spinner.succeed`Ran \"yarn install --check-files\" ${ts()}`;\n } catch (error) {\n spinner.fail();\n ctx.log.error(\"yarn install failed\", { error });\n\n const message = serializeError(error).message;\n if (message) {\n println({ ensureEmptyLineAbove: true, indent: 2 })(message);\n }\n }\n }\n\n return changes;\n }\n\n private async _merge(\n ctx: Context<DevArgs>,\n {\n hashes: { localChanges, environmentChanges, environmentFilesVersion },\n printLocalChangesOptions,\n printEnvironmentChangesOptions,\n }: {\n hashes: FileSyncHashes;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n const conflicts = getConflicts({ localChanges, environmentChanges });\n if (conflicts.size > 0) {\n ctx.log.debug(\"conflicts detected\", { conflicts });\n\n let preference = ctx.args[\"--prefer\"];\n if (!preference) {\n printConflicts({ conflicts });\n preference = await select({ choices: Object.values(MergeConflictPreference) })`\n {bold How should we resolve these conflicts?}\n `;\n }\n\n switch (preference) {\n case MergeConflictPreference.CANCEL: {\n process.exit(0);\n break;\n }\n case MergeConflictPreference.LOCAL: {\n environmentChanges = withoutConflictingChanges({ conflicts, changes: environmentChanges });\n break;\n }\n case MergeConflictPreference.ENVIRONMENT: {\n localChanges = withoutConflictingChanges({ conflicts, changes: localChanges });\n break;\n }\n }\n }\n\n if (environmentChanges.size > 0) {\n await this._getChangesFromEnvironment(ctx, {\n changes: environmentChanges,\n filesVersion: environmentFilesVersion,\n printEnvironmentChangesOptions,\n });\n }\n\n if (localChanges.size > 0) {\n await this._sendChangesToEnvironment(ctx, {\n changes: localChanges,\n expectedFilesVersion: environmentFilesVersion,\n printLocalChangesOptions,\n });\n }\n }\n\n private async _getChangesFromEnvironment(\n ctx: Context<SyncJsonArgs>,\n {\n filesVersion,\n changes,\n printEnvironmentChangesOptions,\n }: {\n filesVersion: bigint;\n changes: Changes | ChangesWithHash;\n printEnvironmentChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n ctx.log.debug(\"getting changes from gadget\", { filesVersion, changes });\n const created = changes.created();\n const updated = changes.updated();\n\n const spinner = spin({ ensureEmptyLineAbove: true })(\n sprintChanges(ctx, {\n changes,\n tense: \"present\",\n title: sprint`Pulling ${pluralize(\"file\", changes.size)}.`,\n ...printEnvironmentChangesOptions,\n }),\n );\n\n try {\n let files: File[] = [];\n if (created.length > 0 || updated.length > 0) {\n const { fileSyncFiles } = await this.syncJson.edit.query({\n query: FILE_SYNC_FILES_QUERY,\n variables: {\n paths: [...created, ...updated],\n filesVersion: String(filesVersion),\n encoding: FileSyncEncoding.Base64,\n },\n });\n\n files = fileSyncFiles.files;\n }\n\n await this.writeToLocalFilesystem(ctx, {\n filesVersion,\n files,\n delete: changes.deleted(),\n spinner,\n printEnvironmentChangesOptions,\n });\n } catch (error) {\n spinner.fail();\n throw error;\n }\n }\n\n private async _sendChangesToEnvironment(\n ctx: Context<SyncJsonArgs>,\n {\n changes,\n expectedFilesVersion = this.syncJson.filesVersion,\n printLocalChangesOptions,\n }: {\n changes: Changes | ChangesWithHash;\n expectedFilesVersion?: bigint;\n printLocalChangesOptions?: Partial<PrintChangesOptions>;\n },\n ): Promise<void> {\n ctx.log.debug(\"sending changes to gadget\", { expectedFilesVersion, changes });\n const changed: FileSyncChangedEventInput[] = [];\n const deleted: FileSyncDeletedEventInput[] = [];\n\n await pMap(changes, async ([normalizedPath, change]) => {\n if (change.type === \"delete\") {\n deleted.push({ path: normalizedPath });\n return;\n }\n\n const absolutePath = this.syncJson.directory.absolute(normalizedPath);\n\n let stats;\n try {\n stats = await fs.stat(absolutePath);\n } catch (error) {\n swallowEnoent(error);\n ctx.log.debug(\"skipping change because file doesn't exist\", { path: normalizedPath });\n return;\n }\n\n let content = \"\";\n if (stats.isFile()) {\n content = await fs.readFile(absolutePath, FileSyncEncoding.Base64);\n }\n\n let oldPath;\n if (change.type === \"create\" && change.oldPath) {\n oldPath = change.oldPath;\n }\n\n changed.push({\n content,\n oldPath,\n path: normalizedPath,\n mode: stats.mode,\n encoding: FileSyncEncoding.Base64,\n });\n });\n\n if (changed.length === 0 && deleted.length === 0) {\n ctx.log.debug(\"skipping send because there are no changes\");\n return;\n }\n\n const contentLength = changed.map((change) => change.content.length).reduce((a, b) => a + b, 0);\n if (contentLength > MAX_PUSH_CONTENT_LENGTH) {\n throw new EdgeCaseError(sprint`\n {underline Your file changes are too large to push.}\n\n Run \"ggt status\" to see your changes and consider\n ignoring some files or pushing in smaller batches.\n `);\n }\n\n const spinner = spin({ ensureEmptyLineAbove: true })(\n sprintChanges(ctx, {\n changes,\n tense: \"present\",\n title: sprintln`Pushing ${pluralize(\"file\", changed.length + deleted.length)}. ${symbol.arrowRight}`,\n ...printLocalChangesOptions,\n }),\n );\n\n try {\n const {\n publishFileSyncEvents: { remoteFilesVersion, problems: filesyncProblems },\n } = await this.syncJson.edit.mutate({\n mutation: PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n variables: {\n input: {\n expectedRemoteFilesVersion: String(expectedFilesVersion),\n changed,\n deleted,\n },\n },\n http: {\n retry: {\n // we can retry this request because\n // expectedRemoteFilesVersion makes it idempotent\n methods: [\"POST\"],\n calculateDelay: ({ error, computedValue }) => {\n if (isFilesVersionMismatchError(error.response?.body)) {\n // don't retry if we get a files version mismatch error\n return 0;\n }\n return computedValue;\n },\n },\n },\n });\n\n if (BigInt(remoteFilesVersion) > expectedFilesVersion + 1n) {\n // we can't save the remoteFilesVersion because we haven't\n // received the intermediate filesVersions yet\n throw new Error(\"Files version mismatch\");\n }\n\n await this.syncJson.save(remoteFilesVersion);\n\n spinner.succeed(\n sprintChanges(ctx, {\n changes,\n tense: \"past\",\n title: sprintln`Pushed ${pluralize(\"file\", changed.length + deleted.length)}. ${symbol.arrowRight} ${ts()}`,\n ...printLocalChangesOptions,\n }),\n );\n\n if (filesyncProblems.length > 0) {\n println({ ensureEmptyLineAbove: true })`\n {red Gadget has detected the following fatal errors with your files:}\n\n ${sprintProblems({\n problems: filesyncProblemsToProblems(filesyncProblems),\n showFileTypes: false,\n indent: 10,\n })}\n\n {red Your app will not be operational until all fatal errors are fixed.}\n `;\n }\n } catch (error) {\n if (isFilesVersionMismatchError(error)) {\n spinner.clear();\n } else {\n spinner.fail();\n }\n\n throw error;\n }\n }\n}\n"],"names":["execa","fs","ms","assert","path","process","pMap","PQueue","pRetry","pluralize","FileSyncEncoding","FILE_SYNC_COMPARISON_HASHES_QUERY","FILE_SYNC_FILES_QUERY","FILE_SYNC_HASHES_QUERY","PUBLISH_FILE_SYNC_EVENTS_MUTATION","REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION","config","confirm","println","filesyncProblemsToProblems","sprintProblems","EdgeCaseError","select","spin","sprint","sprintln","symbol","ts","noop","isEEXISTError","isENOENTError","isENOTDIRError","isENOTEMPTYError","serializeError","Changes","printChanges","sprintChanges","getConflicts","printConflicts","withoutConflictingChanges","supportsPermissions","swallowEnoent","TooManyMergeAttemptsError","isFilesVersionMismatchError","swallowFilesVersionMismatch","getNecessaryChanges","isEqualHashes","MergeConflictPreference","MAX_MERGE_ATTEMPTS","MAX_PUSH_CONTENT_LENGTH","FileSync","hashes","ctx","quietly","spinner","ensureEmptyLineAbove","undefined","localHashes","localFilesVersionHashes","environmentHashes","environmentFilesVersion","Promise","all","syncJson","directory","filesVersion","fileSyncHashes","edit","query","BigInt","fileSyncComparisonHashes","variables","String","filesVersionHashes","latestFilesVersionHashes","inSync","localChanges","from","to","existing","ignore","environmentChanges","size","Array","keys","every","startsWith","localChangesToPush","environmentChangesToPull","onlyDotGadgetFilesChanged","filepath","bothChanged","succeed","error","fail","print","changes","tense","title","idle","_syncOperations","onIdle","mergeChangesWithEnvironment","printLocalChangesOptions","printEnvironmentChangesOptions","add","_sendChangesToEnvironment","merge","subscribeToEnvironmentChanges","beforeChanges","afterChanges","onError","subscribe","subscription","localFilesVersion","onData","remoteFileSyncEvents","changed","deleted","remoteFilesVersion","log","warn","debug","map","change","filterIgnoredFiles","file","ignored","ignores","filter","length","save","writeToLocalFilesystem","files","delete","tick","limit","catch","maxAttempts","attempt","clear","info","_merge","push","force","args","expectedFilesVersion","command","pull","_getChangesFromEnvironment","options","directoriesWithDeletedFiles","Set","pathToDelete","dir","dirname","normalize","currentPath","absolute","backupPath","relative","remove","move","set","type","stats","stat","isDirectory","retries","windows","minTimeout","onFailedAttempt","directoryWithDeletedFile","values","sort","reverse","some","rmdir","absolutePath","pathExists","endsWith","ensureDir","outputFile","Buffer","content","encoding","chmod","mode","loadIgnoreFile","arrowDown","includeDotGadget","has","cwd","message","indent","conflicts","preference","choices","Object","CANCEL","exit","LOCAL","ENVIRONMENT","created","updated","fileSyncFiles","paths","Base64","normalizedPath","isFile","readFile","oldPath","contentLength","reduce","a","b","arrowRight","publishFileSyncEvents","problems","filesyncProblems","mutate","mutation","input","expectedRemoteFilesVersion","http","retry","methods","calculateDelay","computedValue","response","body","Error","showFileTypes","constructor","concurrency"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/filesync/hashes.ts"],"sourcesContent":["import assert from \"node:assert\";\nimport type { Context } from \"../command/context.js\";\nimport { type Create, type Delete, type Update } from \"./changes.js\";\nimport type { Hash, Hashes } from \"./directory.js\";\n\nexport type CreateWithHash = Create & { targetHash: Hash };\nexport type UpdateWithHash = Update & { sourceHash: Hash; targetHash: Hash };\nexport type DeleteWithHash = Delete & { sourceHash: Hash };\nexport type ChangeWithHash = CreateWithHash | UpdateWithHash | DeleteWithHash;\n\nexport class ChangesWithHash extends Map<string, ChangeWithHash> {\n created(): string[] {\n return Array.from(this.entries())\n .filter(([, change]) => change.type === \"create\")\n .map(([path]) => path);\n }\n\n updated(): string[] {\n return Array.from(this.entries())\n .filter(([, change]) => change.type === \"update\")\n .map(([path]) => path);\n }\n\n deleted(): string[] {\n return Array.from(this.entries())\n .filter(([, change]) => change.type === \"delete\")\n .map(([path]) => path);\n }\n}\n\n/**\n * Depending on the callers perspective, this either calculates the\n * changes that were made to `from` to make it end up as `to`, or it\n * calculates the changes that need to be made to `from` to make it end\n * up as `to`.\n *\n * If `existing` is provided, any changes that `existing` already has\n * are skipped.\n *\n * If `ignore` is provided, any changes that were made to a path that\n * starts with any of the `ignore`ed paths are skipped.\n *\n * @param ctx - The current context.\n * @param options - The options to use.\n * @param options.from - The source hashes.\n * @param options.to - The target hashes.\n * @param options.existing - The existing hashes.\n * @param options.ignore - The paths to ignore.\n */\nexport const getNecessaryChanges = (\n ctx: Context,\n {\n from: source,\n to: target,\n existing,\n ignore,\n }: {\n from: Hashes;\n to: Hashes;\n existing?: Hashes;\n ignore?: string[];\n },\n): ChangesWithHash => {\n const changes = new ChangesWithHash();\n const targetPaths = Object.keys(target);\n\n for (const [sourcePath, sourceHash] of Object.entries(source)) {\n if (ignore?.some((ignored) => sourcePath.startsWith(ignored))) {\n continue;\n }\n\n const targetHash = target[sourcePath];\n if (!targetHash) {\n if (!sourcePath.endsWith(\"/\") || !targetPaths.some((targetPath) => targetPath.startsWith(sourcePath))) {\n // sourcePath is a file and it doesn't exist in target OR\n // sourcePath is a directory and target doesn't have any\n // existing files inside it, therefor the sourcePath has been\n // deleted\n changes.set(sourcePath, { type: \"delete\", sourceHash });\n ctx.log.trace(\"file deleted\", { path: sourcePath, sourceHash });\n }\n } else if (!isEqualHash(sourcePath, sourceHash, targetHash)) {\n // the file or directory exists in target, but has a different\n // hash, so it's been updated\n changes.set(sourcePath, { type: \"update\", sourceHash, targetHash });\n ctx.log.trace(\"file updated\", { path: sourcePath, sourceHash, targetHash });\n }\n }\n\n for (const targetPath of targetPaths) {\n if (ignore?.some((ignored) => targetPath.startsWith(ignored))) {\n continue;\n }\n\n if (!source[targetPath]) {\n // the targetPath doesn't exist in source, so it's been created\n const targetHash = target[targetPath];\n assert(targetHash, \"targetHash should exist\");\n\n changes.set(targetPath, { type: \"create\", targetHash });\n ctx.log.trace(\"file created\", { path: targetPath, targetHash });\n }\n }\n\n if (!existing) {\n return changes;\n }\n\n return withoutUnnecessaryChanges(ctx, { changes, existing });\n};\n\n/**\n * Filters out changes that the existing filesystem already has.\n */\nexport const withoutUnnecessaryChanges = (\n ctx: Context,\n { changes, existing }: { changes: ChangesWithHash; existing: Hashes },\n): ChangesWithHash => {\n const necessaryChanges = new ChangesWithHash();\n\n for (const [path, change] of changes) {\n const existingHash = existing[path];\n if (change.type === \"delete\" && !existingHash) {\n // already deleted\n ctx.log.trace(\"already deleted\", { path });\n continue;\n }\n\n if (change.type !== \"delete\" && existingHash && isEqualHash(path, change.targetHash, existingHash)) {\n // already created or updated\n ctx.log.trace(\"already created or updated\", { path, existingHash, targetHash: change.targetHash });\n continue;\n }\n\n // we could do this:\n // if (change.type === \"update\" && !existingHash) {\n // change = { type: \"create\", targetHash: change.targetHash };\n // }\n // but, changing the type makes the output look confusing and it\n // doesn't change the outcome, so we just leave it as is\n\n necessaryChanges.set(path, change);\n }\n\n return necessaryChanges;\n};\n\nexport const isEqualHash = (_path: string, aHash: Hash, bHash: Hash): boolean => {\n // FIXME: we're running into issues syncing permissions with Gadget\n // so we're temporarily disabling this check until we can figure out\n return aHash.sha1 === bHash.sha1;\n\n // if (aHash.sha1 !== bHash.sha1) {\n // // the contents are different\n // return false;\n // }\n\n // if (path.endsWith(\"/\")) {\n // // it's a directory, so we don't care about permissions\n // return true;\n // }\n\n // if (isNil(aHash.permissions) || isNil(bHash.permissions)) {\n // // one of the filesystems doesn't support permissions, so ignore them\n // return true;\n // }\n\n // // the contents are the same, and both filesystems support permissions\n // // so ensure the permissions are also the same\n // return aHash.permissions === bHash.permissions;\n};\n\nexport const isEqualHashes = (ctx: Context, a: Hashes, b: Hashes): boolean => {\n for (const [aPath, aHash] of Object.entries(a)) {\n const bHash = b[aPath];\n if (!bHash || !isEqualHash(aPath, aHash, bHash)) {\n ctx.log.debug(\"hashes are not equal\", { path: aPath, aHash, bHash });\n return false;\n }\n }\n\n for (const bPath of Object.keys(b)) {\n if (!a[bPath]) {\n ctx.log.debug(\"hashes are not equal\", { path: bPath, aHash: undefined, bHash: b[bPath] });\n return false;\n }\n }\n\n return true;\n};\n"],"names":["assert","ChangesWithHash","Map","created","Array","from","entries","filter","change","type","map","path","updated","deleted","getNecessaryChanges","ctx","source","to","target","existing","ignore","changes","targetPaths","Object","keys","sourcePath","sourceHash","some","ignored","startsWith","targetHash","endsWith","targetPath","set","log","trace","isEqualHash","withoutUnnecessaryChanges","necessaryChanges","existingHash","_path","aHash","bHash","sha1","isEqualHashes","a","b","aPath","debug","bPath","undefined"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/services/filesync/hashes.ts"],"sourcesContent":["import assert from \"node:assert\";\nimport type { Context } from \"../command/context.js\";\nimport { type Create, type Delete, type Update } from \"./changes.js\";\nimport type { Hash, Hashes } from \"./directory.js\";\n\nexport type CreateWithHash = Create & { targetHash: Hash };\nexport type UpdateWithHash = Update & { sourceHash: Hash; targetHash: Hash };\nexport type DeleteWithHash = Delete & { sourceHash: Hash };\nexport type ChangeWithHash = CreateWithHash | UpdateWithHash | DeleteWithHash;\n\nexport class ChangesWithHash extends Map<string, ChangeWithHash> {\n created(): string[] {\n return Array.from(this.entries())\n .filter(([, change]) => change.type === \"create\")\n .map(([path]) => path);\n }\n\n updated(): string[] {\n return Array.from(this.entries())\n .filter(([, change]) => change.type === \"update\")\n .map(([path]) => path);\n }\n\n deleted(): string[] {\n return Array.from(this.entries())\n .filter(([, change]) => change.type === \"delete\")\n .map(([path]) => path);\n }\n}\n\n/**\n * Depending on the callers perspective, this either calculates the\n * changes that were made to `from` to make it end up as `to`, or it\n * calculates the changes that need to be made to `from` to make it end\n * up as `to`.\n *\n * If `existing` is provided, any changes that `existing` already has\n * are skipped.\n *\n * If `ignore` is provided, any changes that were made to a path that\n * starts with any of the `ignore`ed paths are skipped.\n *\n * @param ctx - The current context.\n * @param options - The options to use.\n * @param options.from - The source hashes.\n * @param options.to - The target hashes.\n * @param options.existing - The existing hashes.\n * @param options.ignore - The paths to ignore.\n */\nexport const getNecessaryChanges = (\n ctx: Context,\n {\n from: source,\n to: target,\n existing,\n ignore,\n }: {\n from: Hashes;\n to: Hashes;\n existing?: Hashes;\n ignore?: string[];\n },\n): ChangesWithHash => {\n const changes = new ChangesWithHash();\n const targetPaths = Object.keys(target);\n\n for (const [sourcePath, sourceHash] of Object.entries(source)) {\n if (ignore?.some((ignored) => sourcePath.startsWith(ignored))) {\n continue;\n }\n\n const targetHash = target[sourcePath];\n if (!targetHash) {\n if (!sourcePath.endsWith(\"/\") || !targetPaths.some((targetPath) => targetPath.startsWith(sourcePath))) {\n // sourcePath is a file and it doesn't exist in target OR\n // sourcePath is a directory and target doesn't have any\n // existing files inside it, therefor the sourcePath has been\n // deleted\n changes.set(sourcePath, { type: \"delete\", sourceHash });\n ctx.log.trace(\"file deleted\", { path: sourcePath, sourceHash });\n }\n } else if (!isEqualHash(sourcePath, sourceHash, targetHash)) {\n // the file or directory exists in target, but has a different\n // hash, so it's been updated\n changes.set(sourcePath, { type: \"update\", sourceHash, targetHash });\n ctx.log.trace(\"file updated\", { path: sourcePath, sourceHash, targetHash });\n }\n }\n\n for (const targetPath of targetPaths) {\n if (ignore?.some((ignored) => targetPath.startsWith(ignored))) {\n continue;\n }\n\n if (!source[targetPath]) {\n // the targetPath doesn't exist in source, so it's been created\n const targetHash = target[targetPath];\n assert(targetHash, \"targetHash should exist\");\n\n changes.set(targetPath, { type: \"create\", targetHash });\n ctx.log.trace(\"file created\", { path: targetPath, targetHash });\n }\n }\n\n if (!existing) {\n return changes;\n }\n\n return withoutUnnecessaryChanges(ctx, { changes, existing });\n};\n\n/**\n * Filters out changes that the existing filesystem already has.\n */\nexport const withoutUnnecessaryChanges = (\n ctx: Context,\n { changes, existing }: { changes: ChangesWithHash; existing: Hashes },\n): ChangesWithHash => {\n const necessaryChanges = new ChangesWithHash();\n\n for (const [path, change] of changes) {\n const existingHash = existing[path];\n if (change.type === \"delete\" && !existingHash) {\n // already deleted\n ctx.log.trace(\"already deleted\", { path });\n continue;\n }\n\n if (change.type !== \"delete\" && existingHash && isEqualHash(path, change.targetHash, existingHash)) {\n // already created or updated\n ctx.log.trace(\"already created or updated\", { path, existingHash, targetHash: change.targetHash });\n continue;\n }\n\n // we could do this:\n // if (change.type === \"update\" && !existingHash) {\n // change = { type: \"create\", targetHash: change.targetHash };\n // }\n // but, changing the type makes the output look confusing and it\n // doesn't change the outcome, so we just leave it as is\n\n necessaryChanges.set(path, change);\n }\n\n return necessaryChanges;\n};\n\nexport const isEqualHash = (_path: string, aHash: Hash, bHash: Hash): boolean => {\n // FIXME: we're running into issues syncing permissions with Gadget\n // so we're temporarily disabling this check until we can figure out\n return aHash.sha1 === bHash.sha1;\n\n // if (aHash.sha1 !== bHash.sha1) {\n // // the contents are different\n // return false;\n // }\n\n // if (path.endsWith(\"/\")) {\n // // it's a directory, so we don't care about permissions\n // return true;\n // }\n\n // if (isNil(aHash.permissions) || isNil(bHash.permissions)) {\n // // one of the filesystems doesn't support permissions, so ignore them\n // return true;\n // }\n\n // // the contents are the same, and both filesystems support permissions\n // // so ensure the permissions are also the same\n // return aHash.permissions === bHash.permissions;\n};\n\nexport const isEqualHashes = (ctx: Context, a: Hashes, b: Hashes): boolean => {\n for (const [aPath, aHash] of Object.entries(a)) {\n const bHash = b[aPath];\n if (!bHash || !isEqualHash(aPath, aHash, bHash)) {\n ctx.log.debug(\"hashes are not equal\", { path: aPath, aHash, bHash });\n return false;\n }\n }\n\n for (const bPath of Object.keys(b)) {\n if (!a[bPath]) {\n ctx.log.debug(\"hashes are not equal\", { path: bPath, aHash: undefined, bHash: b[bPath] });\n return false;\n }\n }\n\n return true;\n};\n"],"names":["assert","ChangesWithHash","Map","created","Array","from","entries","filter","change","type","map","path","updated","deleted","getNecessaryChanges","ctx","source","to","target","existing","ignore","changes","targetPaths","Object","keys","sourcePath","sourceHash","some","ignored","startsWith","targetHash","endsWith","targetPath","set","log","trace","isEqualHash","withoutUnnecessaryChanges","necessaryChanges","existingHash","_path","aHash","bHash","sha1","isEqualHashes","a","b","aPath","debug","bPath","undefined"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/filesync/strategy.ts"],"sourcesContent":["import { ArgError } from \"../command/arg.js\";\nimport { sprint } from \"../output/sprint.js\";\n\nexport const FileSyncStrategy = Object.freeze({\n CANCEL: \"cancel\",\n MERGE: \"merge\",\n PUSH: \"push\",\n PULL: \"pull\",\n});\n\nexport type FileSyncStrategy = (typeof FileSyncStrategy)[keyof typeof FileSyncStrategy];\n\nexport const MergeConflictPreference = Object.freeze({\n CANCEL: sprint`Cancel (Ctrl+C)`,\n LOCAL: sprint`Keep {underline local} conflicting changes`,\n ENVIRONMENT: sprint`Keep {underline environment}'s conflicting changes`,\n});\n\nexport type MergeConflictPreference = (typeof MergeConflictPreference)[keyof typeof MergeConflictPreference];\n\nexport const MergeConflictPreferenceArg = (value: string, name: string): MergeConflictPreference => {\n if ([\"local\", \"environment\"].includes(value)) {\n return MergeConflictPreference[value.toUpperCase() as keyof typeof MergeConflictPreference];\n }\n\n if (value === \"gadget\") {\n // v0.4 (deprecated)\n return MergeConflictPreference.ENVIRONMENT;\n }\n\n throw new ArgError(sprint`\n ${name} must be {bold local} or {bold environment}\n\n {bold EXAMPLES:}\n ${name}=local\n ${name}=environment\n `);\n};\n\n// export type FileSyncArgs = DevArgs | PushArgs | PullArgs;\n\n// export const getFileSyncStrategy = (ctx: Context<FileSyncArgs>): FileSyncStrategy | undefined => {\n// switch (true) {\n// case ctx.args[\"--push\"]:\n// return FileSyncStrategy.PUSH;\n// case ctx.args[\"--pull\"]:\n// return FileSyncStrategy.PULL;\n// case ctx.args[\"--merge\"]:\n// return FileSyncStrategy.MERGE;\n// default:\n// return undefined;\n// }\n// };\n\n// export const validateFileSyncStrategy = (ctx: Context<FileSyncArgs>): void => {\n// const strategies = [\"--push\", \"--pull\", \"--merge\"] as const;\n// for (const strategy of strategies) {\n// if (!ctx.args[strategy]) {\n// continue;\n// }\n\n// for (const conflicting of strategies.filter((s) => s !== strategy)) {\n// if (ctx.args[conflicting]) {\n// throw new ArgError(sprint`{bold ${strategy}} and {bold ${conflicting}} cannot be used together`);\n// }\n// }\n// }\n// };\n"],"names":["ArgError","sprint","FileSyncStrategy","Object","freeze","CANCEL","MERGE","PUSH","PULL","MergeConflictPreference","LOCAL","ENVIRONMENT","MergeConflictPreferenceArg","value","name","includes","toUpperCase"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,oBAAoB;AAC7C,SAASC,MAAM,QAAQ,sBAAsB;AAE7C,OAAO,MAAMC,mBAAmBC,OAAOC,MAAM,CAAC;IAC5CC,QAAQ;IACRC,OAAO;IACPC,MAAM;IACNC,MAAM;AACR,GAAG;AAIH,OAAO,MAAMC,0BAA0BN,OAAOC,MAAM,CAAC;IACnDC,QAAQJ,MAAM,CAAC,eAAe,CAAC;IAC/BS,OAAOT,MAAM,CAAC,0CAA0C,CAAC;IACzDU,aAAaV,MAAM,CAAC,kDAAkD,CAAC;AACzE,GAAG;AAIH,OAAO,MAAMW,6BAA6B,CAACC,OAAeC;IACxD,IAAI;QAAC;QAAS;KAAc,CAACC,QAAQ,CAACF,QAAQ;QAC5C,OAAOJ,uBAAuB,CAACI,MAAMG,WAAW,GAA2C;IAC7F;IAEA,IAAIH,UAAU,UAAU;QACtB,oBAAoB;QACpB,OAAOJ,wBAAwBE,WAAW;IAC5C;IAEA,MAAM,IAAIX,SAASC,MAAM,CAAC;MACtB,EAAEa,KAAK;;;QAGL,EAAEA,KAAK;QACP,EAAEA,KAAK;IACX,CAAC;AACL,EAAE,CAEF,4DAA4D;CAE5D,qGAAqG;CACrG,oBAAoB;CACpB,+BAA+B;CAC/B,sCAAsC;CACtC,+BAA+B;CAC/B,sCAAsC;CACtC,gCAAgC;CAChC,uCAAuC;CACvC,eAAe;CACf,0BAA0B;CAC1B,MAAM;CACN,KAAK;CAEL,kFAAkF;CAClF,iEAAiE;CACjE,yCAAyC;CACzC,iCAAiC;CACjC,kBAAkB;CAClB,QAAQ;CAER,4EAA4E;CAC5E,qCAAqC;CACrC,4GAA4G;CAC5G,UAAU;CACV,QAAQ;CACR,MAAM;CACN,KAAK"}
1
+ {"version":3,"sources":["../../../src/services/filesync/strategy.ts"],"sourcesContent":["import { ArgError } from \"../command/arg.js\";\nimport { sprint } from \"../output/sprint.js\";\n\nexport const FileSyncStrategy = Object.freeze({\n CANCEL: \"cancel\",\n MERGE: \"merge\",\n PUSH: \"push\",\n PULL: \"pull\",\n});\n\nexport type FileSyncStrategy = (typeof FileSyncStrategy)[keyof typeof FileSyncStrategy];\n\nexport const MergeConflictPreference = Object.freeze({\n CANCEL: sprint`Cancel (Ctrl+C)`,\n LOCAL: sprint`Keep {underline local} conflicting changes`,\n ENVIRONMENT: sprint`Keep {underline environment}'s conflicting changes`,\n});\n\nexport type MergeConflictPreference = (typeof MergeConflictPreference)[keyof typeof MergeConflictPreference];\n\nexport const MergeConflictPreferenceArg = (value: string, name: string): MergeConflictPreference => {\n if ([\"local\", \"environment\"].includes(value)) {\n return MergeConflictPreference[value.toUpperCase() as keyof typeof MergeConflictPreference];\n }\n\n if (value === \"gadget\") {\n // v0.4 (deprecated)\n return MergeConflictPreference.ENVIRONMENT;\n }\n\n throw new ArgError(sprint`\n ${name} must be {bold local} or {bold environment}\n\n {bold EXAMPLES:}\n ${name}=local\n ${name}=environment\n `);\n};\n\n// export type FileSyncArgs = DevArgs | PushArgs | PullArgs;\n\n// export const getFileSyncStrategy = (ctx: Context<FileSyncArgs>): FileSyncStrategy | undefined => {\n// switch (true) {\n// case ctx.args[\"--push\"]:\n// return FileSyncStrategy.PUSH;\n// case ctx.args[\"--pull\"]:\n// return FileSyncStrategy.PULL;\n// case ctx.args[\"--merge\"]:\n// return FileSyncStrategy.MERGE;\n// default:\n// return undefined;\n// }\n// };\n\n// export const validateFileSyncStrategy = (ctx: Context<FileSyncArgs>): void => {\n// const strategies = [\"--push\", \"--pull\", \"--merge\"] as const;\n// for (const strategy of strategies) {\n// if (!ctx.args[strategy]) {\n// continue;\n// }\n\n// for (const conflicting of strategies.filter((s) => s !== strategy)) {\n// if (ctx.args[conflicting]) {\n// throw new ArgError(sprint`{bold ${strategy}} and {bold ${conflicting}} cannot be used together`);\n// }\n// }\n// }\n// };\n"],"names":["ArgError","sprint","FileSyncStrategy","Object","freeze","CANCEL","MERGE","PUSH","PULL","MergeConflictPreference","LOCAL","ENVIRONMENT","MergeConflictPreferenceArg","value","name","includes","toUpperCase"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA,SAASA,QAAQ,QAAQ,oBAAoB;AAC7C,SAASC,MAAM,QAAQ,sBAAsB;AAE7C,OAAO,MAAMC,mBAAmBC,OAAOC,MAAM,CAAC;IAC5CC,QAAQ;IACRC,OAAO;IACPC,MAAM;IACNC,MAAM;AACR,GAAG;AAIH,OAAO,MAAMC,0BAA0BN,OAAOC,MAAM,CAAC;IACnDC,QAAQJ,MAAM,CAAC,eAAe,CAAC;IAC/BS,OAAOT,MAAM,CAAC,0CAA0C,CAAC;IACzDU,aAAaV,MAAM,CAAC,kDAAkD,CAAC;AACzE,GAAG;AAIH,OAAO,MAAMW,6BAA6B,CAACC,OAAeC;IACxD,IAAI;QAAC;QAAS;KAAc,CAACC,QAAQ,CAACF,QAAQ;QAC5C,OAAOJ,uBAAuB,CAACI,MAAMG,WAAW,GAA2C;IAC7F;IAEA,IAAIH,UAAU,UAAU;QACtB,oBAAoB;QACpB,OAAOJ,wBAAwBE,WAAW;IAC5C;IAEA,MAAM,IAAIX,SAASC,MAAM,CAAC;MACtB,EAAEa,KAAK;;;QAGL,EAAEA,KAAK;QACP,EAAEA,KAAK;IACX,CAAC;AACL,EAAE,CAEF,4DAA4D;CAE5D,qGAAqG;CACrG,oBAAoB;CACpB,+BAA+B;CAC/B,sCAAsC;CACtC,+BAA+B;CAC/B,sCAAsC;CACtC,gCAAgC;CAChC,uCAAuC;CACvC,eAAe;CACf,0BAA0B;CAC1B,MAAM;CACN,KAAK;CAEL,kFAAkF;CAClF,iEAAiE;CACjE,yCAAyC;CACzC,iCAAiC;CACjC,kBAAkB;CAClB,QAAQ;CAER,4EAA4E;CAC5E,qCAAqC;CACrC,4GAA4G;CAC5G,UAAU;CACV,QAAQ;CACR,MAAM;CACN,KAAK"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/filesync/sync-json.ts"],"sourcesContent":["import { findUp } from \"find-up\";\nimport fs from \"fs-extra\";\nimport assert from \"node:assert\";\nimport path from \"node:path\";\nimport { simpleGit } from \"simple-git\";\nimport terminalLink from \"terminal-link\";\nimport { z } from \"zod\";\nimport { EnvironmentType, getApps, type Application, type Environment } from \"../app/app.js\";\nimport { AppArg } from \"../app/arg.js\";\nimport { Edit } from \"../app/edit/edit.js\";\nimport { ArgError, type ArgsDefinition } from \"../command/arg.js\";\nimport type { Context } from \"../command/context.js\";\nimport { config, homePath } from \"../config/config.js\";\nimport { println } from \"../output/print.js\";\nimport { select } from \"../output/select.js\";\nimport { sprint, sprintln, type SprintOptions } from \"../output/sprint.js\";\nimport { getUserOrLogin } from \"../user/user.js\";\nimport { sortBySimilar } from \"../util/collection.js\";\nimport { defaults } from \"../util/object.js\";\nimport { Directory } from \"./directory.js\";\nimport { UnknownDirectoryError } from \"./error.js\";\n\nexport const SyncJsonArgs = {\n \"--app\": { type: AppArg, alias: [\"-a\", \"--application\"] },\n \"--env\": { type: String, alias: [\"-e\", \"--environment\"] },\n \"--allow-unknown-directory\": Boolean,\n \"--allow-different-app\": Boolean,\n} satisfies ArgsDefinition;\n\nexport type SyncJsonArgs = typeof SyncJsonArgs;\n\n/**\n * The state of the filesystem.\n *\n * This is persisted to `.gadget/sync.json` within the {@linkcode directory}.\n */\n// TODO: rename and/or add to ctx?\nexport class SyncJson {\n /**\n * The {@linkcode Application} that the directory is synced to.\n */\n readonly app: Application;\n\n /**\n * The {@linkcode Environment} that the directory is synced to.\n */\n readonly env: Environment;\n\n /**\n * The last git branch that was checked out in the directory.\n */\n gitBranch: string | undefined;\n\n /**\n * The {@linkcode Edit} client that can be used to send Gadget API\n * requests to the environment that the directory is synced to.\n */\n readonly edit: Edit;\n\n /**\n * The last time the `.gadget/sync.json` file was modified.\n *\n * @deprecated\n * We don't use this anymore, it's only here because older versions\n * of ggt expect it to be in the .gadget/sync.json file.\n */\n private _mtime: number | undefined;\n\n private constructor(\n /**\n * The {@linkcode Context} that was used to initialize this\n * {@linkcode SyncJson} instance.\n */\n readonly ctx: Context<SyncJsonArgs>,\n\n /**\n * The root directory of the local filesystem, or in other words,\n * the directory that contains the `.gadget/sync.json` file.\n */\n readonly directory: Directory,\n\n /**\n * Indicates whether the environment was changed when this instance\n * was loaded or initialized.\n */\n readonly previousEnvironment: string | undefined,\n\n /**\n * The state of the `.gadget/sync.json` file on the local\n * filesystem.\n */\n readonly state: SyncJsonState,\n ) {\n this.ctx = ctx.child({\n fields: () => ({\n syncJson: {\n directory: this.directory.path,\n branch: this.gitBranch,\n ...this.state,\n },\n }),\n });\n\n assert(this.ctx.app, \"app must be set on syncJson context\");\n this.app = this.ctx.app;\n\n assert(this.ctx.env, \"env must be set on syncJson context\");\n this.env = this.ctx.env;\n\n this.edit = new Edit(this.ctx);\n }\n\n /**\n * The last filesVersion that was written to the filesystem.\n *\n * This determines if the filesystem in Gadget is ahead of the\n * filesystem on the local machine.\n */\n get filesVersion(): bigint {\n const environment = this.state.environments[this.state.environment];\n assert(environment, \"environment must exist in environments\");\n return BigInt(environment.filesVersion);\n }\n\n /**\n * Loads a {@linkcode SyncJson} from the specified directory.\n *\n * Returns undefined if the directory doesn't exist, is empty, or\n * doesn't contain a `.gadget/sync.json` file.\n */\n static async load(ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<SyncJson | undefined> {\n ctx = ctx.child({ name: \"sync-json\" });\n\n const user = await getUserOrLogin(ctx);\n const availableApps = await getApps(ctx);\n if (availableApps.length === 0) {\n throw new ArgError(\n sprint`\n You (${user.email}) don't have have any Gadget applications.\n\n Visit https://gadget.new to create one!\n `,\n );\n }\n\n // try to load the .gadget/sync.json file\n const syncJsonFile = await fs\n .readFile(directory.absolute(\".gadget/sync.json\"), \"utf8\")\n .catch((error: unknown) => ctx.log.warn(\"failed to read .gadget/sync.json\", { error }));\n\n if (!syncJsonFile) {\n // the .gadget/sync.json file doesn't exist in the directory\n // or any of its parents\n return undefined;\n }\n\n // the .gadget/sync.json file exists, try to parse it\n let state: SyncJsonState | undefined;\n try {\n state = SyncJsonState.parse(JSON.parse(syncJsonFile));\n } catch (error) {\n // the .gadget/sync.json file exists, but it's invalid\n ctx.log.warn(\"failed to parse .gadget/sync.json\", { error, syncJsonFile });\n return undefined;\n }\n\n ctx.app = await loadApp(ctx, { availableApps, state });\n ctx.env = await loadEnv(ctx, { app: ctx.app, state });\n\n if (state.application !== ctx.app.slug) {\n // .gadget/sync.json is associated with a different app\n if (ctx.args[\"--allow-different-app\"]) {\n // the user passed --allow-different-app, so use the application\n // and environment they specified and clobber everything\n const syncJson = new SyncJson(ctx, directory, undefined, {\n application: ctx.app.slug,\n environment: ctx.env.name,\n environments: {\n [ctx.env.name]: { filesVersion: \"0\" },\n },\n });\n\n await syncJson.loadGitBranch();\n return syncJson;\n }\n\n // the user didn't pass --allow-different-app, so throw an error\n throw new ArgError(sprint`\n You were about to sync the following app to the following directory:\n\n ${ctx.app.slug} (${ctx.env.name}) → ${directory.path}\n\n However, that directory has already been synced with this app:\n\n ${state.application} (${state.environment})\n\n If you're sure that you want to sync:\n\n ${ctx.app.slug} (${ctx.env.name}) → ${directory.path}\n\n Run \"ggt dev\" with the {bold --allow-different-app} flag.\n `);\n }\n\n let previousEnvironment: string | undefined;\n if (state.environment !== ctx.env.name) {\n // the user specified a different environment, update the state\n println({ ensureEmptyLineAbove: true })`\n Changing environment.\n\n ${state.environment} → ${ctx.env.name}\n `;\n\n previousEnvironment = state.environment;\n state.environment = ctx.env.name;\n if (!state.environments[ctx.env.name]) {\n // the user has never synced to this environment before\n state.environments[ctx.env.name] = { filesVersion: \"0\" };\n }\n }\n\n const syncJson = new SyncJson(ctx, directory, previousEnvironment, state);\n await syncJson.save(syncJson.filesVersion);\n await syncJson.loadGitBranch();\n return syncJson;\n }\n\n /**\n * Loads a {@linkcode SyncJson} from the specified directory or\n * initializes a new one.\n *\n * - Ensures a user is logged in.\n * - Ensures the user has at least one application.\n * - Ensures the directory exists.\n * - Ensures the directory is empty or contains a `.gadget/sync.json` file, unless --allow-unknown-directory was passed\n * - Ensures the specified app matches the app the directory previously synced to, unless --allow-different-app was passed\n */\n // TODO: rename to loadOrAskAndInit\n static async loadOrInit(ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<SyncJson> {\n ctx = ctx.child({ name: \"sync-json\" });\n\n let syncJson = await SyncJson.load(ctx, { directory });\n if (syncJson) {\n // the .gadget/sync.json file already exists and is valid\n return syncJson;\n }\n\n if ((await directory.hasFiles()) && !ctx.args[\"--allow-unknown-directory\"]) {\n // the directory isn't empty and the user didn't pass --allow-unknown-directory\n throw new UnknownDirectoryError(ctx, { directory });\n }\n\n ctx.app = await loadApp(ctx, { availableApps: await getApps(ctx) });\n ctx.env = await loadEnv(ctx, { app: ctx.app });\n\n // the directory is empty or the user passed\n // --allow-unknown-directory, either way ensure the directory exists\n // and create a fresh .gadget/sync.json file\n await fs.ensureDir(directory.path);\n\n syncJson = new SyncJson(ctx, directory, undefined, {\n application: ctx.app.slug,\n environment: ctx.env.name,\n environments: {\n [ctx.env.name]: { filesVersion: \"0\" },\n },\n });\n\n await syncJson.save(syncJson.filesVersion);\n await syncJson.loadGitBranch();\n\n return syncJson;\n }\n\n // TODO: just asks the user to select an app and environment, doesn't create a .gadget/sync.json file\n // static async loadOrAsk(ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<SyncJson | undefined> {\n // }\n\n /**\n * Updates {@linkcode _syncJson} and saves it to `.gadget/sync.json`.\n */\n async save(filesVersion: string | bigint): Promise<void> {\n const environment = this.state.environments[this.state.environment];\n assert(environment, \"environment must exist in environments\");\n environment.filesVersion = String(filesVersion);\n\n this.ctx.log.debug(\"saving .gadget/sync.json\");\n this._mtime = Date.now();\n await fs.outputJSON(\n this.directory.absolute(\".gadget/sync.json\"),\n {\n ...this.state,\n\n // v0.4\n app: this.state.application,\n filesVersion: String(filesVersion),\n mtime: this._mtime,\n },\n { spaces: 2 },\n );\n }\n\n async loadGitBranch(): Promise<void> {\n this.gitBranch = await loadBranch(this.ctx, { directory: this.directory });\n }\n\n sprint(options: SprintOptions = {}): string {\n let str = sprintln`\n Application ${this.app.slug}\n Environment ${this.env.name}\n `;\n\n if (this.gitBranch) {\n str += sprintln({ indent: 5 })`Branch ${this.gitBranch}`;\n }\n\n if (terminalLink.isSupported) {\n str += sprintln({ ensureEmptyLineAbove: true })`\n ${terminalLink(\"Preview\", `https://${this.app.slug}--${this.env.name}.gadget.app`)} ${terminalLink(\"Editor\", `https://${this.app.primaryDomain}/edit/${this.env.name}`)} ${terminalLink(\"Playground\", `https://${this.app.primaryDomain}/api/playground/graphql?environment=${this.env.name}`)} ${terminalLink(\"Docs\", `https://docs.gadget.dev/api/${this.app.slug}`)}\n `;\n } else {\n str += sprintln`\n ------------------------\n Preview https://${this.app.slug}--${this.env.name}.gadget.app\n Editor https://${this.app.primaryDomain}/edit/${this.env.name}\n Playground https://${this.app.primaryDomain}/api/playground/graphql?environment=${this.env.name}\n Docs https://docs.gadget.dev/api/${this.app.slug}\n `;\n }\n\n return sprintln(options)(str);\n }\n\n print(options?: SprintOptions): void {\n options = defaults(options, { ensureEmptyLineAbove: true });\n println(this.sprint(options));\n }\n}\n\nexport const loadSyncJsonDirectory = async (dir: string): Promise<Directory> => {\n if (config.windows && dir.startsWith(\"~/\")) {\n // \"~\" doesn't expand to the home directory on Windows\n dir = homePath(dir.slice(2));\n }\n\n // TODO: rename to .gadget/ggt.json\n const syncJsonPath = await findUp(\".gadget/sync.json\", { cwd: dir });\n if (syncJsonPath) {\n // we found a .gadget/sync.json file, use its parent directory\n dir = path.join(syncJsonPath, \"../..\");\n }\n\n // ensure the directory path is absolute\n dir = path.resolve(dir);\n\n return await Directory.init(dir);\n};\n\n// ensure the selected app is valid\nconst loadApp = async (\n ctx: Context<SyncJsonArgs>,\n { availableApps, state }: { availableApps: Application[]; state?: SyncJsonState },\n): Promise<Application> => {\n let appSlug = ctx.args[\"--app\"] || state?.application;\n if (!appSlug) {\n // the user didn't specify an app, ask them to select one\n appSlug = await select({ choices: availableApps.map((x) => x.slug) })`\n Which application do you want to develop?\n `;\n }\n\n const app = availableApps.find((app) => app.slug === appSlug);\n if (app) {\n // the user specified an app or we loaded it from the state,\n // and it exists in their list of applications, so return it\n return app;\n }\n\n // the specified appSlug doesn't exist in their list of apps,\n // either they misspelled it or they don't have access to it\n // anymore, suggest some apps that are similar to the one they\n // specified\n const similarAppSlugs = sortBySimilar(\n appSlug,\n availableApps.map((app) => app.slug),\n ).slice(0, 5);\n\n // TODO: differentiate between incorrect --app vs state.application?\n throw new ArgError(\n sprint`\n Unknown application:\n\n ${appSlug}\n\n Did you mean one of these?\n\n • ${similarAppSlugs.join(\"\\n • \")}\n `,\n );\n};\n\nconst loadEnv = async (ctx: Context<SyncJsonArgs>, { app, state }: { app: Application; state?: SyncJsonState }): Promise<Environment> => {\n if (ctx.args[\"--env\"] && !app.multiEnvironmentEnabled) {\n // this is a legacy app that only has 1 development environment, so\n // let them know now rather than running into a weird error later\n // TODO: come back to this\n throw new ArgError(\n sprint`\n You specified an environment but your app doesn't have multiple environments.\n\n Remove the \"--env\" flag to sync to the {bold ${app.primaryDomain}} environment.\n `,\n );\n }\n\n const devEnvs = app.environments.filter((env) => env.type === EnvironmentType.Development);\n\n let envName = ctx.args[\"--env\"] || state?.environment;\n if (!envName) {\n // user didn't specify an environment, ask them to select one\n envName = await select({ choices: devEnvs.map((x) => x.name) })`\n Which environment do you want to develop on?\n `;\n }\n\n if (envName.toLowerCase() === \"production\") {\n // specifically call out that they can't dev, push, or pull to prod\n throw new ArgError(\n sprint`\n You cannot \"ggt ${ctx.command}\" your {bold production} environment.\n `,\n );\n }\n\n const env = devEnvs.find((env) => env.name === envName.toLowerCase());\n if (env) {\n // the user specified an environment or we loaded it from the state,\n // and it exists in the app's list of environments, so return it\n return env;\n }\n\n // the specified env doesn't exist in their list of environments,\n // either they misspelled it or they don't have access to it\n // anymore, suggest some envs that are similar to the one they\n // specified\n const similarEnvironments = sortBySimilar(\n envName,\n devEnvs.map((env) => env.name),\n ).slice(0, 5);\n\n throw new ArgError(\n sprint`\n Unknown environment:\n\n ${envName}\n\n Did you mean one of these?\n\n • ${similarEnvironments.join(\"\\n • \")}\n `,\n );\n};\n\n/**\n * Returns the current git branch of the directory or undefined if\n * the directory isn't a git repository.\n */\nconst loadBranch = async (ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<string | undefined> => {\n try {\n const branch = await simpleGit(directory.path).revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch;\n } catch (error) {\n ctx.log.warn(\"failed to read git branch\", { error });\n return undefined;\n }\n};\n\nexport const SyncJsonStateV05 = z.object({\n application: z.string(),\n environment: z.string(),\n environments: z.record(z.object({ filesVersion: z.string() })),\n});\n\nexport const SyncJsonStateV04 = z.object({\n app: z.string(),\n filesVersion: z.string(),\n mtime: z.number(),\n});\n\nexport const AnySyncJsonState = z.union([SyncJsonStateV05, SyncJsonStateV04]);\n\nexport const SyncJsonState = AnySyncJsonState.transform((state): SyncJsonStateV05 => {\n if (\"environment\" in state) {\n // it's a v0.5 state\n return state;\n }\n\n // it's a v0.4 state, transform it to a v0.5 state\n return {\n application: state.app,\n environment: \"development\",\n environments: { development: { filesVersion: state.filesVersion } },\n };\n});\n\nexport type SyncJsonStateV05 = z.infer<typeof SyncJsonStateV05>;\nexport type SyncJsonStateV04 = z.infer<typeof SyncJsonStateV04>;\nexport type AnySyncJsonState = z.infer<typeof AnySyncJsonState>;\nexport type SyncJsonState = z.infer<typeof SyncJsonState>;\n"],"names":["findUp","fs","assert","path","simpleGit","terminalLink","z","EnvironmentType","getApps","AppArg","Edit","ArgError","config","homePath","println","select","sprint","sprintln","getUserOrLogin","sortBySimilar","defaults","Directory","UnknownDirectoryError","SyncJsonArgs","type","alias","String","Boolean","SyncJson","filesVersion","environment","state","environments","BigInt","load","ctx","directory","child","name","user","availableApps","length","email","syncJsonFile","readFile","absolute","catch","error","log","warn","undefined","SyncJsonState","parse","JSON","app","loadApp","env","loadEnv","application","slug","args","syncJson","loadGitBranch","previousEnvironment","ensureEmptyLineAbove","save","loadOrInit","hasFiles","ensureDir","debug","_mtime","Date","now","outputJSON","mtime","spaces","gitBranch","loadBranch","options","str","indent","isSupported","primaryDomain","print","edit","fields","branch","loadSyncJsonDirectory","dir","windows","startsWith","slice","syncJsonPath","cwd","join","resolve","init","appSlug","choices","map","x","find","similarAppSlugs","multiEnvironmentEnabled","devEnvs","filter","Development","envName","toLowerCase","command","similarEnvironments","revparse","SyncJsonStateV05","object","string","record","SyncJsonStateV04","number","AnySyncJsonState","union","transform","development"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/services/filesync/sync-json.ts"],"sourcesContent":["import { findUp } from \"find-up\";\nimport fs from \"fs-extra\";\nimport assert from \"node:assert\";\nimport path from \"node:path\";\nimport { simpleGit } from \"simple-git\";\nimport terminalLink from \"terminal-link\";\nimport { z } from \"zod\";\nimport { EnvironmentType, getApps, type Application, type Environment } from \"../app/app.js\";\nimport { AppArg } from \"../app/arg.js\";\nimport { Edit } from \"../app/edit/edit.js\";\nimport { ArgError, type ArgsDefinition } from \"../command/arg.js\";\nimport type { Context } from \"../command/context.js\";\nimport { config, homePath } from \"../config/config.js\";\nimport { println } from \"../output/print.js\";\nimport { select } from \"../output/select.js\";\nimport { sprint, sprintln, type SprintOptions } from \"../output/sprint.js\";\nimport { getUserOrLogin } from \"../user/user.js\";\nimport { sortBySimilar } from \"../util/collection.js\";\nimport { defaults } from \"../util/object.js\";\nimport { Directory } from \"./directory.js\";\nimport { UnknownDirectoryError } from \"./error.js\";\n\nexport const SyncJsonArgs = {\n \"--app\": { type: AppArg, alias: [\"-a\", \"--application\"] },\n \"--env\": { type: String, alias: [\"-e\", \"--environment\"] },\n \"--allow-unknown-directory\": Boolean,\n \"--allow-different-app\": Boolean,\n} satisfies ArgsDefinition;\n\nexport type SyncJsonArgs = typeof SyncJsonArgs;\n\n/**\n * The state of the filesystem.\n *\n * This is persisted to `.gadget/sync.json` within the {@linkcode directory}.\n */\n// TODO: rename and/or add to ctx?\nexport class SyncJson {\n /**\n * The {@linkcode Application} that the directory is synced to.\n */\n readonly app: Application;\n\n /**\n * The {@linkcode Environment} that the directory is synced to.\n */\n readonly env: Environment;\n\n /**\n * The last git branch that was checked out in the directory.\n */\n gitBranch: string | undefined;\n\n /**\n * The {@linkcode Edit} client that can be used to send Gadget API\n * requests to the environment that the directory is synced to.\n */\n readonly edit: Edit;\n\n /**\n * The last time the `.gadget/sync.json` file was modified.\n *\n * @deprecated\n * We don't use this anymore, it's only here because older versions\n * of ggt expect it to be in the .gadget/sync.json file.\n */\n private _mtime: number | undefined;\n\n private constructor(\n /**\n * The {@linkcode Context} that was used to initialize this\n * {@linkcode SyncJson} instance.\n */\n readonly ctx: Context<SyncJsonArgs>,\n\n /**\n * The root directory of the local filesystem, or in other words,\n * the directory that contains the `.gadget/sync.json` file.\n */\n readonly directory: Directory,\n\n /**\n * Indicates whether the environment was changed when this instance\n * was loaded or initialized.\n */\n readonly previousEnvironment: string | undefined,\n\n /**\n * The state of the `.gadget/sync.json` file on the local\n * filesystem.\n */\n readonly state: SyncJsonState,\n ) {\n this.ctx = ctx.child({\n fields: () => ({\n syncJson: {\n directory: this.directory.path,\n branch: this.gitBranch,\n ...this.state,\n },\n }),\n });\n\n assert(this.ctx.app, \"app must be set on syncJson context\");\n this.app = this.ctx.app;\n\n assert(this.ctx.env, \"env must be set on syncJson context\");\n this.env = this.ctx.env;\n\n this.edit = new Edit(this.ctx);\n }\n\n /**\n * The last filesVersion that was written to the filesystem.\n *\n * This determines if the filesystem in Gadget is ahead of the\n * filesystem on the local machine.\n */\n get filesVersion(): bigint {\n const environment = this.state.environments[this.state.environment];\n assert(environment, \"environment must exist in environments\");\n return BigInt(environment.filesVersion);\n }\n\n /**\n * Loads a {@linkcode SyncJson} from the specified directory.\n *\n * Returns undefined if the directory doesn't exist, is empty, or\n * doesn't contain a `.gadget/sync.json` file.\n */\n static async load(ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<SyncJson | undefined> {\n ctx = ctx.child({ name: \"sync-json\" });\n\n const user = await getUserOrLogin(ctx);\n const availableApps = await getApps(ctx);\n if (availableApps.length === 0) {\n throw new ArgError(\n sprint`\n You (${user.email}) don't have have any Gadget applications.\n\n Visit https://gadget.new to create one!\n `,\n );\n }\n\n // try to load the .gadget/sync.json file\n const syncJsonFile = await fs\n .readFile(directory.absolute(\".gadget/sync.json\"), \"utf8\")\n .catch((error: unknown) => ctx.log.warn(\"failed to read .gadget/sync.json\", { error }));\n\n if (!syncJsonFile) {\n // the .gadget/sync.json file doesn't exist in the directory\n // or any of its parents\n return undefined;\n }\n\n // the .gadget/sync.json file exists, try to parse it\n let state: SyncJsonState | undefined;\n try {\n state = SyncJsonState.parse(JSON.parse(syncJsonFile));\n } catch (error) {\n // the .gadget/sync.json file exists, but it's invalid\n ctx.log.warn(\"failed to parse .gadget/sync.json\", { error, syncJsonFile });\n return undefined;\n }\n\n ctx.app = await loadApp(ctx, { availableApps, state });\n ctx.env = await loadEnv(ctx, { app: ctx.app, state });\n\n if (state.application !== ctx.app.slug) {\n // .gadget/sync.json is associated with a different app\n if (ctx.args[\"--allow-different-app\"]) {\n // the user passed --allow-different-app, so use the application\n // and environment they specified and clobber everything\n const syncJson = new SyncJson(ctx, directory, undefined, {\n application: ctx.app.slug,\n environment: ctx.env.name,\n environments: {\n [ctx.env.name]: { filesVersion: \"0\" },\n },\n });\n\n await syncJson.loadGitBranch();\n return syncJson;\n }\n\n // the user didn't pass --allow-different-app, so throw an error\n throw new ArgError(sprint`\n You were about to sync the following app to the following directory:\n\n ${ctx.app.slug} (${ctx.env.name}) → ${directory.path}\n\n However, that directory has already been synced with this app:\n\n ${state.application} (${state.environment})\n\n If you're sure that you want to sync:\n\n ${ctx.app.slug} (${ctx.env.name}) → ${directory.path}\n\n Run \"ggt dev\" with the {bold --allow-different-app} flag.\n `);\n }\n\n let previousEnvironment: string | undefined;\n if (state.environment !== ctx.env.name) {\n // the user specified a different environment, update the state\n println({ ensureEmptyLineAbove: true })`\n Changing environment.\n\n ${state.environment} → ${ctx.env.name}\n `;\n\n previousEnvironment = state.environment;\n state.environment = ctx.env.name;\n if (!state.environments[ctx.env.name]) {\n // the user has never synced to this environment before\n state.environments[ctx.env.name] = { filesVersion: \"0\" };\n }\n }\n\n const syncJson = new SyncJson(ctx, directory, previousEnvironment, state);\n await syncJson.save(syncJson.filesVersion);\n await syncJson.loadGitBranch();\n return syncJson;\n }\n\n /**\n * Loads a {@linkcode SyncJson} from the specified directory or\n * initializes a new one.\n *\n * - Ensures a user is logged in.\n * - Ensures the user has at least one application.\n * - Ensures the directory exists.\n * - Ensures the directory is empty or contains a `.gadget/sync.json` file, unless --allow-unknown-directory was passed\n * - Ensures the specified app matches the app the directory previously synced to, unless --allow-different-app was passed\n */\n // TODO: rename to loadOrAskAndInit\n static async loadOrInit(ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<SyncJson> {\n ctx = ctx.child({ name: \"sync-json\" });\n\n let syncJson = await SyncJson.load(ctx, { directory });\n if (syncJson) {\n // the .gadget/sync.json file already exists and is valid\n return syncJson;\n }\n\n if ((await directory.hasFiles()) && !ctx.args[\"--allow-unknown-directory\"]) {\n // the directory isn't empty and the user didn't pass --allow-unknown-directory\n throw new UnknownDirectoryError(ctx, { directory });\n }\n\n ctx.app = await loadApp(ctx, { availableApps: await getApps(ctx) });\n ctx.env = await loadEnv(ctx, { app: ctx.app });\n\n // the directory is empty or the user passed\n // --allow-unknown-directory, either way ensure the directory exists\n // and create a fresh .gadget/sync.json file\n await fs.ensureDir(directory.path);\n\n syncJson = new SyncJson(ctx, directory, undefined, {\n application: ctx.app.slug,\n environment: ctx.env.name,\n environments: {\n [ctx.env.name]: { filesVersion: \"0\" },\n },\n });\n\n await syncJson.save(syncJson.filesVersion);\n await syncJson.loadGitBranch();\n\n return syncJson;\n }\n\n // TODO: just asks the user to select an app and environment, doesn't create a .gadget/sync.json file\n // static async loadOrAsk(ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<SyncJson | undefined> {\n // }\n\n /**\n * Updates {@linkcode _syncJson} and saves it to `.gadget/sync.json`.\n */\n async save(filesVersion: string | bigint): Promise<void> {\n const environment = this.state.environments[this.state.environment];\n assert(environment, \"environment must exist in environments\");\n environment.filesVersion = String(filesVersion);\n\n this.ctx.log.debug(\"saving .gadget/sync.json\");\n this._mtime = Date.now();\n await fs.outputJSON(\n this.directory.absolute(\".gadget/sync.json\"),\n {\n ...this.state,\n\n // v0.4\n app: this.state.application,\n filesVersion: String(filesVersion),\n mtime: this._mtime,\n },\n { spaces: 2 },\n );\n }\n\n async loadGitBranch(): Promise<void> {\n this.gitBranch = await loadBranch(this.ctx, { directory: this.directory });\n }\n\n sprint(options: SprintOptions = {}): string {\n let str = sprintln`\n Application ${this.app.slug}\n Environment ${this.env.name}\n `;\n\n if (this.gitBranch) {\n str += sprintln({ indent: 5 })`Branch ${this.gitBranch}`;\n }\n\n if (terminalLink.isSupported) {\n str += sprintln({ ensureEmptyLineAbove: true })`\n ${terminalLink(\"Preview\", `https://${this.app.slug}--${this.env.name}.gadget.app`)} ${terminalLink(\"Editor\", `https://${this.app.primaryDomain}/edit/${this.env.name}`)} ${terminalLink(\"Playground\", `https://${this.app.primaryDomain}/api/playground/graphql?environment=${this.env.name}`)} ${terminalLink(\"Docs\", `https://docs.gadget.dev/api/${this.app.slug}`)}\n `;\n } else {\n str += sprintln`\n ------------------------\n Preview https://${this.app.slug}--${this.env.name}.gadget.app\n Editor https://${this.app.primaryDomain}/edit/${this.env.name}\n Playground https://${this.app.primaryDomain}/api/playground/graphql?environment=${this.env.name}\n Docs https://docs.gadget.dev/api/${this.app.slug}\n `;\n }\n\n return sprintln(options)(str);\n }\n\n print(options?: SprintOptions): void {\n options = defaults(options, { ensureEmptyLineAbove: true });\n println(this.sprint(options));\n }\n}\n\nexport const loadSyncJsonDirectory = async (dir: string): Promise<Directory> => {\n if (config.windows && dir.startsWith(\"~/\")) {\n // \"~\" doesn't expand to the home directory on Windows\n dir = homePath(dir.slice(2));\n }\n\n // TODO: rename to .gadget/ggt.json\n const syncJsonPath = await findUp(\".gadget/sync.json\", { cwd: dir });\n if (syncJsonPath) {\n // we found a .gadget/sync.json file, use its parent directory\n dir = path.join(syncJsonPath, \"../..\");\n }\n\n // ensure the directory path is absolute\n dir = path.resolve(dir);\n\n return await Directory.init(dir);\n};\n\n// ensure the selected app is valid\nconst loadApp = async (\n ctx: Context<SyncJsonArgs>,\n { availableApps, state }: { availableApps: Application[]; state?: SyncJsonState },\n): Promise<Application> => {\n let appSlug = ctx.args[\"--app\"] || state?.application;\n if (!appSlug) {\n // the user didn't specify an app, ask them to select one\n appSlug = await select({ choices: availableApps.map((x) => x.slug) })`\n Which application do you want to develop?\n `;\n }\n\n const app = availableApps.find((app) => app.slug === appSlug);\n if (app) {\n // the user specified an app or we loaded it from the state,\n // and it exists in their list of applications, so return it\n return app;\n }\n\n // the specified appSlug doesn't exist in their list of apps,\n // either they misspelled it or they don't have access to it\n // anymore, suggest some apps that are similar to the one they\n // specified\n const similarAppSlugs = sortBySimilar(\n appSlug,\n availableApps.map((app) => app.slug),\n ).slice(0, 5);\n\n // TODO: differentiate between incorrect --app vs state.application?\n throw new ArgError(\n sprint`\n Unknown application:\n\n ${appSlug}\n\n Did you mean one of these?\n\n • ${similarAppSlugs.join(\"\\n • \")}\n `,\n );\n};\n\nconst loadEnv = async (ctx: Context<SyncJsonArgs>, { app, state }: { app: Application; state?: SyncJsonState }): Promise<Environment> => {\n if (ctx.args[\"--env\"] && !app.multiEnvironmentEnabled) {\n // this is a legacy app that only has 1 development environment, so\n // let them know now rather than running into a weird error later\n // TODO: come back to this\n throw new ArgError(\n sprint`\n You specified an environment but your app doesn't have multiple environments.\n\n Remove the \"--env\" flag to sync to the {bold ${app.primaryDomain}} environment.\n `,\n );\n }\n\n const devEnvs = app.environments.filter((env) => env.type === EnvironmentType.Development);\n\n let envName = ctx.args[\"--env\"] || state?.environment;\n if (!envName) {\n // user didn't specify an environment, ask them to select one\n envName = await select({ choices: devEnvs.map((x) => x.name) })`\n Which environment do you want to develop on?\n `;\n }\n\n if (envName.toLowerCase() === \"production\") {\n // specifically call out that they can't dev, push, or pull to prod\n throw new ArgError(\n sprint`\n You cannot \"ggt ${ctx.command}\" your {bold production} environment.\n `,\n );\n }\n\n const env = devEnvs.find((env) => env.name === envName.toLowerCase());\n if (env) {\n // the user specified an environment or we loaded it from the state,\n // and it exists in the app's list of environments, so return it\n return env;\n }\n\n // the specified env doesn't exist in their list of environments,\n // either they misspelled it or they don't have access to it\n // anymore, suggest some envs that are similar to the one they\n // specified\n const similarEnvironments = sortBySimilar(\n envName,\n devEnvs.map((env) => env.name),\n ).slice(0, 5);\n\n throw new ArgError(\n sprint`\n Unknown environment:\n\n ${envName}\n\n Did you mean one of these?\n\n • ${similarEnvironments.join(\"\\n • \")}\n `,\n );\n};\n\n/**\n * Returns the current git branch of the directory or undefined if\n * the directory isn't a git repository.\n */\nconst loadBranch = async (ctx: Context<SyncJsonArgs>, { directory }: { directory: Directory }): Promise<string | undefined> => {\n try {\n const branch = await simpleGit(directory.path).revparse([\"--abbrev-ref\", \"HEAD\"]);\n return branch;\n } catch (error) {\n ctx.log.warn(\"failed to read git branch\", { error });\n return undefined;\n }\n};\n\nexport const SyncJsonStateV05 = z.object({\n application: z.string(),\n environment: z.string(),\n environments: z.record(z.object({ filesVersion: z.string() })),\n});\n\nexport const SyncJsonStateV04 = z.object({\n app: z.string(),\n filesVersion: z.string(),\n mtime: z.number(),\n});\n\nexport const AnySyncJsonState = z.union([SyncJsonStateV05, SyncJsonStateV04]);\n\nexport const SyncJsonState = AnySyncJsonState.transform((state): SyncJsonStateV05 => {\n if (\"environment\" in state) {\n // it's a v0.5 state\n return state;\n }\n\n // it's a v0.4 state, transform it to a v0.5 state\n return {\n application: state.app,\n environment: \"development\",\n environments: { development: { filesVersion: state.filesVersion } },\n };\n});\n\nexport type SyncJsonStateV05 = z.infer<typeof SyncJsonStateV05>;\nexport type SyncJsonStateV04 = z.infer<typeof SyncJsonStateV04>;\nexport type AnySyncJsonState = z.infer<typeof AnySyncJsonState>;\nexport type SyncJsonState = z.infer<typeof SyncJsonState>;\n"],"names":["findUp","fs","assert","path","simpleGit","terminalLink","z","EnvironmentType","getApps","AppArg","Edit","ArgError","config","homePath","println","select","sprint","sprintln","getUserOrLogin","sortBySimilar","defaults","Directory","UnknownDirectoryError","SyncJsonArgs","type","alias","String","Boolean","SyncJson","filesVersion","environment","state","environments","BigInt","load","ctx","directory","child","name","user","availableApps","length","email","syncJsonFile","readFile","absolute","catch","error","log","warn","undefined","SyncJsonState","parse","JSON","app","loadApp","env","loadEnv","application","slug","args","syncJson","loadGitBranch","previousEnvironment","ensureEmptyLineAbove","save","loadOrInit","hasFiles","ensureDir","debug","_mtime","Date","now","outputJSON","mtime","spaces","gitBranch","loadBranch","options","str","indent","isSupported","primaryDomain","print","edit","fields","branch","loadSyncJsonDirectory","dir","windows","startsWith","slice","syncJsonPath","cwd","join","resolve","init","appSlug","choices","map","x","find","similarAppSlugs","multiEnvironmentEnabled","devEnvs","filter","Development","envName","toLowerCase","command","similarEnvironments","revparse","SyncJsonStateV05","object","string","record","SyncJsonStateV04","number","AnySyncJsonState","union","transform","development"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/http/auth.ts"],"sourcesContent":["import { HTTPError, type OptionsInit } from \"got\";\nimport type { Context } from \"../command/context.js\";\nimport { config } from \"../config/config.js\";\nimport { createLogger } from \"../output/log/logger.js\";\nimport { readSession, readToken } from \"../user/session.js\";\n\nconst log = createLogger({ name: \"auth\" });\n\n/**\n * Determines whether the given request options are for a Gadget\n * Services request.\n *\n * @param options - The request options to check.\n * @returns True if the request options are for a Gadget Services\n * request, false otherwise.\n */\nexport const isGadgetServicesRequest = (options: OptionsInit): boolean => {\n return options.url instanceof URL && options.url.host === config.domains.services;\n};\n\n/**\n * Loads the cookie from the session.\n *\n * @returns The cookie string or undefined if there is no session.\n */\nexport const loadCookie = (): string | undefined => {\n const token = readSession();\n return token && `session=${encodeURIComponent(token)};`;\n};\n\n/**\n * Loads the authentication headers.\n *\n * @returns The authentication headers as a record of key-value pairs, or undefined if no headers are available.\n */\nexport const loadAuthHeaders = (): Record<string, string> | undefined => {\n const cookie = loadCookie();\n if (cookie) {\n log.trace(\"loading cookie as auth header\", { cookie });\n return { cookie };\n }\n\n const token = readToken();\n\n if (token) {\n log.trace(\"loading token as auth header\", { token });\n return { \"x-platform-access-token\": token };\n }\n\n return undefined;\n};\n\nexport const isUnauthorizedError = (error: unknown): error is HTTPError => {\n return error instanceof HTTPError && error.response.statusCode === 401;\n};\n\n/**\n * Swallows unauthorized errors and logs a warning, rethrows all other\n * errors.\n *\n * @param ctx - The current context.\n * @param error - The error to handle.\n */\nexport const swallowUnauthorized = (ctx: Context, error: unknown): void => {\n if (isUnauthorizedError(error)) {\n ctx.log.warn(\"swallowing unauthorized error\", { error });\n return;\n }\n throw error;\n};\n"],"names":["HTTPError","config","createLogger","readSession","readToken","log","name","isGadgetServicesRequest","options","url","URL","host","domains","services","loadCookie","token","encodeURIComponent","loadAuthHeaders","cookie","trace","undefined","isUnauthorizedError","error","response","statusCode","swallowUnauthorized","ctx","warn"],"mappings":"AAAA,SAASA,SAAS,QAA0B,MAAM;AAElD,SAASC,MAAM,QAAQ,sBAAsB;AAC7C,SAASC,YAAY,QAAQ,0BAA0B;AACvD,SAASC,WAAW,EAAEC,SAAS,QAAQ,qBAAqB;AAE5D,MAAMC,MAAMH,aAAa;IAAEI,MAAM;AAAO;AAExC;;;;;;;CAOC,GACD,OAAO,MAAMC,0BAA0B,CAACC;IACtC,OAAOA,QAAQC,GAAG,YAAYC,OAAOF,QAAQC,GAAG,CAACE,IAAI,KAAKV,OAAOW,OAAO,CAACC,QAAQ;AACnF,EAAE;AAEF;;;;CAIC,GACD,OAAO,MAAMC,aAAa;IACxB,MAAMC,QAAQZ;IACd,OAAOY,SAAS,CAAC,QAAQ,EAAEC,mBAAmBD,OAAO,CAAC,CAAC;AACzD,EAAE;AAEF;;;;CAIC,GACD,OAAO,MAAME,kBAAkB;IAC7B,MAAMC,SAASJ;IACf,IAAII,QAAQ;QACVb,IAAIc,KAAK,CAAC,iCAAiC;YAAED;QAAO;QACpD,OAAO;YAAEA;QAAO;IAClB;IAEA,MAAMH,QAAQX;IAEd,IAAIW,OAAO;QACTV,IAAIc,KAAK,CAAC,gCAAgC;YAAEJ;QAAM;QAClD,OAAO;YAAE,2BAA2BA;QAAM;IAC5C;IAEA,OAAOK;AACT,EAAE;AAEF,OAAO,MAAMC,sBAAsB,CAACC;IAClC,OAAOA,iBAAiBtB,aAAasB,MAAMC,QAAQ,CAACC,UAAU,KAAK;AACrE,EAAE;AAEF;;;;;;CAMC,GACD,OAAO,MAAMC,sBAAsB,CAACC,KAAcJ;IAChD,IAAID,oBAAoBC,QAAQ;QAC9BI,IAAIrB,GAAG,CAACsB,IAAI,CAAC,iCAAiC;YAAEL;QAAM;QACtD;IACF;IACA,MAAMA;AACR,EAAE"}
1
+ {"version":3,"sources":["../../../src/services/http/auth.ts"],"sourcesContent":["import { HTTPError, type OptionsInit } from \"got\";\nimport type { Context } from \"../command/context.js\";\nimport { config } from \"../config/config.js\";\nimport { createLogger } from \"../output/log/logger.js\";\nimport { readSession, readToken } from \"../user/session.js\";\n\nconst log = createLogger({ name: \"auth\" });\n\n/**\n * Determines whether the given request options are for a Gadget\n * Services request.\n *\n * @param options - The request options to check.\n * @returns True if the request options are for a Gadget Services\n * request, false otherwise.\n */\nexport const isGadgetServicesRequest = (options: OptionsInit): boolean => {\n return options.url instanceof URL && options.url.host === config.domains.services;\n};\n\n/**\n * Loads the cookie from the session.\n *\n * @returns The cookie string or undefined if there is no session.\n */\nexport const loadCookie = (): string | undefined => {\n const token = readSession();\n return token && `session=${encodeURIComponent(token)};`;\n};\n\n/**\n * Loads the authentication headers.\n *\n * @returns The authentication headers as a record of key-value pairs, or undefined if no headers are available.\n */\nexport const loadAuthHeaders = (): Record<string, string> | undefined => {\n const cookie = loadCookie();\n if (cookie) {\n log.trace(\"loading cookie as auth header\", { cookie });\n return { cookie };\n }\n\n const token = readToken();\n\n if (token) {\n log.trace(\"loading token as auth header\", { token });\n return { \"x-platform-access-token\": token };\n }\n\n return undefined;\n};\n\nexport const isUnauthorizedError = (error: unknown): error is HTTPError => {\n return error instanceof HTTPError && error.response.statusCode === 401;\n};\n\n/**\n * Swallows unauthorized errors and logs a warning, rethrows all other\n * errors.\n *\n * @param ctx - The current context.\n * @param error - The error to handle.\n */\nexport const swallowUnauthorized = (ctx: Context, error: unknown): void => {\n if (isUnauthorizedError(error)) {\n ctx.log.warn(\"swallowing unauthorized error\", { error });\n return;\n }\n throw error;\n};\n"],"names":["HTTPError","config","createLogger","readSession","readToken","log","name","isGadgetServicesRequest","options","url","URL","host","domains","services","loadCookie","token","encodeURIComponent","loadAuthHeaders","cookie","trace","undefined","isUnauthorizedError","error","response","statusCode","swallowUnauthorized","ctx","warn"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"AAAA,SAASA,SAAS,QAA0B,MAAM;AAElD,SAASC,MAAM,QAAQ,sBAAsB;AAC7C,SAASC,YAAY,QAAQ,0BAA0B;AACvD,SAASC,WAAW,EAAEC,SAAS,QAAQ,qBAAqB;AAE5D,MAAMC,MAAMH,aAAa;IAAEI,MAAM;AAAO;AAExC;;;;;;;CAOC,GACD,OAAO,MAAMC,0BAA0B,CAACC;IACtC,OAAOA,QAAQC,GAAG,YAAYC,OAAOF,QAAQC,GAAG,CAACE,IAAI,KAAKV,OAAOW,OAAO,CAACC,QAAQ;AACnF,EAAE;AAEF;;;;CAIC,GACD,OAAO,MAAMC,aAAa;IACxB,MAAMC,QAAQZ;IACd,OAAOY,SAAS,CAAC,QAAQ,EAAEC,mBAAmBD,OAAO,CAAC,CAAC;AACzD,EAAE;AAEF;;;;CAIC,GACD,OAAO,MAAME,kBAAkB;IAC7B,MAAMC,SAASJ;IACf,IAAII,QAAQ;QACVb,IAAIc,KAAK,CAAC,iCAAiC;YAAED;QAAO;QACpD,OAAO;YAAEA;QAAO;IAClB;IAEA,MAAMH,QAAQX;IAEd,IAAIW,OAAO;QACTV,IAAIc,KAAK,CAAC,gCAAgC;YAAEJ;QAAM;QAClD,OAAO;YAAE,2BAA2BA;QAAM;IAC5C;IAEA,OAAOK;AACT,EAAE;AAEF,OAAO,MAAMC,sBAAsB,CAACC;IAClC,OAAOA,iBAAiBtB,aAAasB,MAAMC,QAAQ,CAACC,UAAU,KAAK;AACrE,EAAE;AAEF;;;;;;CAMC,GACD,OAAO,MAAMC,sBAAsB,CAACC,KAAcJ;IAChD,IAAID,oBAAoBC,QAAQ;QAC9BI,IAAIrB,GAAG,CAACsB,IAAI,CAAC,iCAAiC;YAAEL;QAAM;QACtD;IACF;IACA,MAAMA;AACR,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/services/http/http.ts"],"sourcesContent":["import { got, type OptionsInit } from \"got\";\nimport ms from \"ms\";\nimport assert from \"node:assert\";\nimport { Agent as HttpAgent } from \"node:http\";\nimport { Agent as HttpsAgent } from \"node:https\";\nimport { Context } from \"../command/context.js\";\nimport { config } from \"../config/config.js\";\nimport { sprint } from \"../output/sprint.js\";\nimport { writeSession } from \"../user/session.js\";\nimport { serializeError } from \"../util/object.js\";\nimport { isGadgetServicesRequest } from \"./auth.js\";\n\nexport type HttpOptions = OptionsInit;\n\nconst getContext = (options: HttpOptions): Context => {\n assert(\n options.context?.[\"ctx\"] instanceof Context,\n sprint(`\n ctx must be provided to http requests:\n\n const response = await http({\n context: { ctx },\n ...options,\n });\n `),\n );\n\n return options.context[\"ctx\"] as Context;\n};\n\n/**\n * An instance of the `got` library with hooks for logging and handling\n * 401 errors. This should be used for all HTTP requests.\n */\nexport const http = got.extend({\n agent: {\n http: new HttpAgent({ keepAlive: true }),\n https: new HttpsAgent({ keepAlive: true }),\n },\n retry: {\n limit: 10,\n methods: [\"GET\", \"PUT\", \"HEAD\", \"DELETE\", \"OPTIONS\", \"TRACE\"],\n statusCodes: [408, 413, 429, 500, 502, 503, 504, 521, 522, 524],\n errorCodes: [\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"EADDRINUSE\",\n \"ECONNREFUSED\",\n \"EPIPE\",\n \"ENOTFOUND\",\n \"ENETUNREACH\",\n \"EAI_AGAIN\",\n \"EADDRNOTAVAIL\",\n \"EHOSTUNREACH\",\n ],\n maxRetryAfter: undefined,\n calculateDelay: ({ computedValue }) => computedValue,\n backoffLimit: ms(\"5s\"),\n noise: 100,\n },\n hooks: {\n beforeRequest: [\n (options) => {\n const ctx = getContext(options);\n options.signal = ctx.signal;\n options.headers[\"user-agent\"] = config.versionFull;\n ctx.log.debug(\"http request\", {\n http: {\n request: {\n method: options.method,\n url: options.url?.toString(),\n },\n },\n });\n },\n ],\n beforeRetry: [\n (error, retryCount) => {\n const ctx = getContext(error.request?.options ?? error.options.context);\n\n ctx.log.warn(\"http request failed, retrying...\", {\n http: {\n retryCount,\n error: serializeError(error),\n request: error.request && {\n method: error.request.options.method,\n url: error.request.options.url?.toString(),\n },\n response: error.response && {\n statusCode: error.response.statusCode,\n traceId: error.response.headers[\"x-trace-id\"],\n durationMs: error.response.timings.phases.total,\n },\n },\n });\n },\n ],\n afterResponse: [\n (response) => {\n const ctx = getContext(response.request.options);\n ctx.log.debug(\"http response\", {\n http: {\n request: {\n method: response.request.options.method,\n url: response.request.options.url?.toString(),\n },\n response: {\n statusCode: response.statusCode,\n traceId: response.headers[\"x-trace-id\"],\n durationMs: response.timings.phases.total,\n },\n },\n });\n\n if (response.statusCode === 401 && isGadgetServicesRequest(response.request.options)) {\n // clear the session if the request was unauthorized\n writeSession(undefined);\n }\n\n return response;\n },\n ],\n },\n});\n"],"names":["got","ms","assert","Agent","HttpAgent","HttpsAgent","Context","config","sprint","writeSession","serializeError","isGadgetServicesRequest","getContext","options","context","http","extend","agent","keepAlive","https","retry","limit","methods","statusCodes","errorCodes","maxRetryAfter","undefined","calculateDelay","computedValue","backoffLimit","noise","hooks","beforeRequest","ctx","signal","headers","versionFull","log","debug","request","method","url","toString","beforeRetry","error","retryCount","warn","response","statusCode","traceId","durationMs","timings","phases","total","afterResponse"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/services/http/http.ts"],"sourcesContent":["import { got, type OptionsInit } from \"got\";\nimport ms from \"ms\";\nimport assert from \"node:assert\";\nimport { Agent as HttpAgent } from \"node:http\";\nimport { Agent as HttpsAgent } from \"node:https\";\nimport { Context } from \"../command/context.js\";\nimport { config } from \"../config/config.js\";\nimport { sprint } from \"../output/sprint.js\";\nimport { writeSession } from \"../user/session.js\";\nimport { serializeError } from \"../util/object.js\";\nimport { isGadgetServicesRequest } from \"./auth.js\";\n\nexport type HttpOptions = OptionsInit;\n\nconst getContext = (options: HttpOptions): Context => {\n assert(\n options.context?.[\"ctx\"] instanceof Context,\n sprint(`\n ctx must be provided to http requests:\n\n const response = await http({\n context: { ctx },\n ...options,\n });\n `),\n );\n\n return options.context[\"ctx\"] as Context;\n};\n\n/**\n * An instance of the `got` library with hooks for logging and handling\n * 401 errors. This should be used for all HTTP requests.\n */\nexport const http = got.extend({\n agent: {\n http: new HttpAgent({ keepAlive: true }),\n https: new HttpsAgent({ keepAlive: true }),\n },\n retry: {\n limit: 10,\n methods: [\"GET\", \"PUT\", \"HEAD\", \"DELETE\", \"OPTIONS\", \"TRACE\"],\n statusCodes: [408, 413, 429, 500, 502, 503, 504, 521, 522, 524],\n errorCodes: [\n \"ETIMEDOUT\",\n \"ECONNRESET\",\n \"EADDRINUSE\",\n \"ECONNREFUSED\",\n \"EPIPE\",\n \"ENOTFOUND\",\n \"ENETUNREACH\",\n \"EAI_AGAIN\",\n \"EADDRNOTAVAIL\",\n \"EHOSTUNREACH\",\n ],\n maxRetryAfter: undefined,\n calculateDelay: ({ computedValue }) => computedValue,\n backoffLimit: ms(\"5s\"),\n noise: 100,\n },\n hooks: {\n beforeRequest: [\n (options) => {\n const ctx = getContext(options);\n options.signal = ctx.signal;\n options.headers[\"user-agent\"] = config.versionFull;\n ctx.log.debug(\"http request\", {\n http: {\n request: {\n method: options.method,\n url: options.url?.toString(),\n },\n },\n });\n },\n ],\n beforeRetry: [\n (error, retryCount) => {\n const ctx = getContext(error.request?.options ?? error.options.context);\n\n ctx.log.warn(\"http request failed, retrying...\", {\n http: {\n retryCount,\n error: serializeError(error),\n request: error.request && {\n method: error.request.options.method,\n url: error.request.options.url?.toString(),\n },\n response: error.response && {\n statusCode: error.response.statusCode,\n traceId: error.response.headers[\"x-trace-id\"],\n durationMs: error.response.timings.phases.total,\n },\n },\n });\n },\n ],\n afterResponse: [\n (response) => {\n const ctx = getContext(response.request.options);\n ctx.log.debug(\"http response\", {\n http: {\n request: {\n method: response.request.options.method,\n url: response.request.options.url?.toString(),\n },\n response: {\n statusCode: response.statusCode,\n traceId: response.headers[\"x-trace-id\"],\n durationMs: response.timings.phases.total,\n },\n },\n });\n\n if (response.statusCode === 401 && isGadgetServicesRequest(response.request.options)) {\n // clear the session if the request was unauthorized\n writeSession(undefined);\n }\n\n return response;\n },\n ],\n },\n});\n"],"names":["got","ms","assert","Agent","HttpAgent","HttpsAgent","Context","config","sprint","writeSession","serializeError","isGadgetServicesRequest","getContext","options","context","http","extend","agent","keepAlive","https","retry","limit","methods","statusCodes","errorCodes","maxRetryAfter","undefined","calculateDelay","computedValue","backoffLimit","noise","hooks","beforeRequest","ctx","signal","headers","versionFull","log","debug","request","method","url","toString","beforeRetry","error","retryCount","warn","response","statusCode","traceId","durationMs","timings","phases","total","afterResponse"],"rangeMappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;","mappings":"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"}