@gadgetinc/ggt 0.3.2 → 0.3.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 (50) hide show
  1. package/README.md +1 -1
  2. package/lib/commands/list.js +11 -8
  3. package/lib/commands/list.js.map +1 -1
  4. package/lib/commands/root.js +3 -3
  5. package/lib/commands/root.js.map +1 -1
  6. package/lib/commands/sync.js +38 -38
  7. package/lib/commands/sync.js.map +1 -1
  8. package/lib/services/app.js +1 -2
  9. package/lib/services/app.js.map +1 -1
  10. package/lib/services/args.js +3 -3
  11. package/lib/services/args.js.map +1 -1
  12. package/lib/services/collections.js +17 -0
  13. package/lib/services/collections.js.map +1 -0
  14. package/lib/services/config.js +5 -6
  15. package/lib/services/config.js.map +1 -1
  16. package/lib/services/debounce.js +21 -0
  17. package/lib/services/debounce.js.map +1 -0
  18. package/lib/services/defaults.js +8 -0
  19. package/lib/services/defaults.js.map +1 -0
  20. package/lib/services/edit-graphql.js +20 -11
  21. package/lib/services/edit-graphql.js.map +1 -1
  22. package/lib/services/errors.js +37 -34
  23. package/lib/services/errors.js.map +1 -1
  24. package/lib/services/filesync.js +33 -33
  25. package/lib/services/filesync.js.map +1 -1
  26. package/lib/services/{fs-utils.js → fs.js} +17 -15
  27. package/lib/services/fs.js.map +1 -0
  28. package/lib/services/is.js +39 -0
  29. package/lib/services/is.js.map +1 -0
  30. package/lib/services/log.js +5 -5
  31. package/lib/services/log.js.map +1 -1
  32. package/lib/services/noop.js +4 -0
  33. package/lib/services/noop.js.map +1 -0
  34. package/lib/services/output.js +21 -6
  35. package/lib/services/output.js.map +1 -1
  36. package/lib/services/promise.js +5 -3
  37. package/lib/services/promise.js.map +1 -1
  38. package/lib/services/prompt.js +22 -0
  39. package/lib/services/prompt.js.map +1 -0
  40. package/lib/services/session.js +6 -2
  41. package/lib/services/session.js.map +1 -1
  42. package/lib/services/sleep.js +8 -6
  43. package/lib/services/sleep.js.map +1 -1
  44. package/lib/services/user.js +4 -6
  45. package/lib/services/user.js.map +1 -1
  46. package/lib/services/version.js +2 -2
  47. package/lib/services/version.js.map +1 -1
  48. package/npm-shrinkwrap.json +503 -473
  49. package/package.json +9 -11
  50. package/lib/services/fs-utils.js.map +0 -1
package/README.md CHANGED
@@ -56,7 +56,7 @@ $ ggt
56
56
  The command-line interface for Gadget
57
57
 
58
58
  VERSION
59
- ggt/0.3.2 linux-x64 node-v16.20.2
59
+ ggt/0.3.3 linux-x64 node-v16.20.2
60
60
 
61
61
  USAGE
62
62
  $ ggt [COMMAND]
@@ -1,4 +1,3 @@
1
- import _ from "lodash";
2
1
  import { getApps } from "../services/app.js";
3
2
  import { println, sprint } from "../services/output.js";
4
3
  import { getUserOrLogin } from "../services/user.js";
@@ -19,7 +18,7 @@ export const usage = sprint`
19
18
  export const run = async ()=>{
20
19
  const user = await getUserOrLogin();
21
20
  const apps = await getApps(user);
22
- if (!apps.length) {
21
+ if (apps.length === 0) {
23
22
  println`
24
23
  It doesn't look like you have any applications.
25
24
 
@@ -27,12 +26,16 @@ export const run = async ()=>{
27
26
  `;
28
27
  return;
29
28
  }
30
- const longestSlug = _.maxBy(apps, "slug.length")?.slug.length ?? 0;
31
- const longestDomain = _.maxBy(apps, "primaryDomain.length")?.primaryDomain.length ?? 0;
32
- println`{bold Slug}${_.repeat(" ", longestSlug - 4)} {bold Domain}`;
33
- println`${_.repeat("─", Math.max(longestSlug, 4))} ${_.repeat("─", Math.max(longestDomain, 6))}`;
34
- for (const app of _.sortBy(apps, "slug")){
35
- println`${app.slug}${_.repeat(" ", longestSlug - app.slug.length)} ${app.primaryDomain}`;
29
+ let longestSlug = 0;
30
+ let longestDomain = 0;
31
+ for (const app of apps){
32
+ longestSlug = Math.max(longestSlug, app.slug.length);
33
+ longestDomain = Math.max(longestDomain, app.primaryDomain.length);
34
+ }
35
+ println`{bold Slug}${" ".repeat(longestSlug - 4)} {bold Domain}`;
36
+ println`${"─".repeat(Math.max(longestSlug, 4))} ${"─".repeat(Math.max(longestDomain, 6))}`;
37
+ for (const app of apps.sort((a, b)=>a.slug.localeCompare(b.slug))){
38
+ println`${app.slug}${" ".repeat(longestSlug - app.slug.length)} ${app.primaryDomain}`;
36
39
  }
37
40
  };
38
41
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/list.ts"],"sourcesContent":["import _ from \"lodash\";\nimport { getApps } from \"../services/app.js\";\nimport { println, sprint } from \"../services/output.js\";\nimport { getUserOrLogin } from \"../services/user.js\";\n\nexport const usage = sprint`\n List the apps available to the currently logged in user.\n\n {bold USAGE}\n $ ggt list\n\n {bold EXAMPLE}\n {gray $ ggt list}\n Slug Domain\n ─────── ──────────────────\n my-app my-app.gadget.app\n example example.gadget.app\n test test.gadget.app\n`;\n\nexport const run = async () => {\n const user = await getUserOrLogin();\n\n const apps = await getApps(user);\n if (!apps.length) {\n println`\n It doesn't look like you have any applications.\n\n Visit https://gadget.new to create one!\n `;\n return;\n }\n\n const longestSlug = _.maxBy(apps, \"slug.length\")?.slug.length ?? 0;\n const longestDomain = _.maxBy(apps, \"primaryDomain.length\")?.primaryDomain.length ?? 0;\n\n println`{bold Slug}${_.repeat(\" \", longestSlug - 4)} {bold Domain}`;\n println`${_.repeat(\"─\", Math.max(longestSlug, 4))} ${_.repeat(\"─\", Math.max(longestDomain, 6))}`;\n for (const app of _.sortBy(apps, \"slug\")) {\n println`${app.slug}${_.repeat(\" \", longestSlug - app.slug.length)} ${app.primaryDomain}`;\n }\n};\n"],"names":["_","getApps","println","sprint","getUserOrLogin","usage","run","user","apps","length","longestSlug","maxBy","slug","longestDomain","primaryDomain","repeat","Math","max","app","sortBy"],"mappings":"AAAA,OAAOA,OAAO,SAAS;AACvB,SAASC,OAAO,QAAQ,qBAAqB;AAC7C,SAASC,OAAO,EAAEC,MAAM,QAAQ,wBAAwB;AACxD,SAASC,cAAc,QAAQ,sBAAsB;AAErD,OAAO,MAAMC,QAAQF,MAAM,CAAC;;;;;;;;;;;;;AAa5B,CAAC,CAAC;AAEF,OAAO,MAAMG,MAAM;IACjB,MAAMC,OAAO,MAAMH;IAEnB,MAAMI,OAAO,MAAMP,QAAQM;IAC3B,IAAI,CAACC,KAAKC,MAAM,EAAE;QAChBP,OAAO,CAAC;;;;IAIR,CAAC;QACD;IACF;IAEA,MAAMQ,cAAcV,EAAEW,KAAK,CAACH,MAAM,gBAAgBI,KAAKH,UAAU;IACjE,MAAMI,gBAAgBb,EAAEW,KAAK,CAACH,MAAM,yBAAyBM,cAAcL,UAAU;IAErFP,OAAO,CAAC,WAAW,EAAEF,EAAEe,MAAM,CAAC,KAAKL,cAAc,GAAG,cAAc,CAAC;IACnER,OAAO,CAAC,EAAEF,EAAEe,MAAM,CAAC,KAAKC,KAAKC,GAAG,CAACP,aAAa,IAAI,CAAC,EAAEV,EAAEe,MAAM,CAAC,KAAKC,KAAKC,GAAG,CAACJ,eAAe,IAAI,CAAC;IAChG,KAAK,MAAMK,OAAOlB,EAAEmB,MAAM,CAACX,MAAM,QAAS;QACxCN,OAAO,CAAC,EAAEgB,IAAIN,IAAI,CAAC,EAAEZ,EAAEe,MAAM,CAAC,KAAKL,cAAcQ,IAAIN,IAAI,CAACH,MAAM,EAAE,CAAC,EAAES,IAAIJ,aAAa,CAAC,CAAC;IAC1F;AACF,EAAE"}
1
+ {"version":3,"sources":["../../src/commands/list.ts"],"sourcesContent":["import { getApps } from \"../services/app.js\";\nimport { println, sprint } from \"../services/output.js\";\nimport { getUserOrLogin } from \"../services/user.js\";\n\nexport const usage = sprint`\n List the apps available to the currently logged in user.\n\n {bold USAGE}\n $ ggt list\n\n {bold EXAMPLE}\n {gray $ ggt list}\n Slug Domain\n ─────── ──────────────────\n my-app my-app.gadget.app\n example example.gadget.app\n test test.gadget.app\n`;\n\nexport const run = async () => {\n const user = await getUserOrLogin();\n\n const apps = await getApps(user);\n if (apps.length === 0) {\n println`\n It doesn't look like you have any applications.\n\n Visit https://gadget.new to create one!\n `;\n return;\n }\n\n let longestSlug = 0;\n let longestDomain = 0;\n\n for (const app of apps) {\n longestSlug = Math.max(longestSlug, app.slug.length);\n longestDomain = Math.max(longestDomain, app.primaryDomain.length);\n }\n\n println`{bold Slug}${\" \".repeat(longestSlug - 4)} {bold Domain}`;\n println`${\"─\".repeat(Math.max(longestSlug, 4))} ${\"─\".repeat(Math.max(longestDomain, 6))}`;\n for (const app of apps.sort((a, b) => a.slug.localeCompare(b.slug))) {\n println`${app.slug}${\" \".repeat(longestSlug - app.slug.length)} ${app.primaryDomain}`;\n }\n};\n"],"names":["getApps","println","sprint","getUserOrLogin","usage","run","user","apps","length","longestSlug","longestDomain","app","Math","max","slug","primaryDomain","repeat","sort","a","b","localeCompare"],"mappings":"AAAA,SAASA,OAAO,QAAQ,qBAAqB;AAC7C,SAASC,OAAO,EAAEC,MAAM,QAAQ,wBAAwB;AACxD,SAASC,cAAc,QAAQ,sBAAsB;AAErD,OAAO,MAAMC,QAAQF,MAAM,CAAC;;;;;;;;;;;;;AAa5B,CAAC,CAAC;AAEF,OAAO,MAAMG,MAAM;IACjB,MAAMC,OAAO,MAAMH;IAEnB,MAAMI,OAAO,MAAMP,QAAQM;IAC3B,IAAIC,KAAKC,MAAM,KAAK,GAAG;QACrBP,OAAO,CAAC;;;;IAIR,CAAC;QACD;IACF;IAEA,IAAIQ,cAAc;IAClB,IAAIC,gBAAgB;IAEpB,KAAK,MAAMC,OAAOJ,KAAM;QACtBE,cAAcG,KAAKC,GAAG,CAACJ,aAAaE,IAAIG,IAAI,CAACN,MAAM;QACnDE,gBAAgBE,KAAKC,GAAG,CAACH,eAAeC,IAAII,aAAa,CAACP,MAAM;IAClE;IAEAP,OAAO,CAAC,WAAW,EAAE,IAAIe,MAAM,CAACP,cAAc,GAAG,cAAc,CAAC;IAChER,OAAO,CAAC,EAAE,IAAIe,MAAM,CAACJ,KAAKC,GAAG,CAACJ,aAAa,IAAI,CAAC,EAAE,IAAIO,MAAM,CAACJ,KAAKC,GAAG,CAACH,eAAe,IAAI,CAAC;IAC1F,KAAK,MAAMC,OAAOJ,KAAKU,IAAI,CAAC,CAACC,GAAGC,IAAMD,EAAEJ,IAAI,CAACM,aAAa,CAACD,EAAEL,IAAI,GAAI;QACnEb,OAAO,CAAC,EAAEU,IAAIG,IAAI,CAAC,EAAE,IAAIE,MAAM,CAACP,cAAcE,IAAIG,IAAI,CAACN,MAAM,EAAE,CAAC,EAAEG,IAAII,aAAa,CAAC,CAAC;IACvF;AACF,EAAE"}
@@ -1,9 +1,9 @@
1
1
  import arg from "arg";
2
2
  import debug from "debug";
3
- import _ from "lodash";
4
3
  import { parseBoolean } from "../services/args.js";
5
4
  import { config } from "../services/config.js";
6
5
  import { CLIError } from "../services/errors.js";
6
+ import { isNil } from "../services/is.js";
7
7
  import { println, sortByLevenshtein, sprint } from "../services/output.js";
8
8
  import { warnIfUpdateAvailable } from "../services/version.js";
9
9
  import { availableCommands } from "./index.js";
@@ -51,11 +51,11 @@ export const run = async ()=>{
51
51
  process.exit(0);
52
52
  }
53
53
  const command = rootArgs._.shift();
54
- if (_.isNil(command)) {
54
+ if (isNil(command)) {
55
55
  println(usage);
56
56
  process.exit(0);
57
57
  }
58
- if (!_.includes(availableCommands, command)) {
58
+ if (!availableCommands.includes(command)) {
59
59
  const [closest] = sortByLevenshtein(command, availableCommands);
60
60
  println`
61
61
  Unknown command {yellow ${command}}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/root.ts"],"sourcesContent":["import arg from \"arg\";\nimport debug from \"debug\";\nimport _ from \"lodash\";\nimport { parseBoolean } from \"../services/args.js\";\nimport { config } from \"../services/config.js\";\nimport { CLIError } from \"../services/errors.js\";\nimport { println, sortByLevenshtein, sprint } from \"../services/output.js\";\nimport { warnIfUpdateAvailable } from \"../services/version.js\";\nimport { availableCommands, type Command } from \"./index.js\";\n\nexport const usage = sprint`\n The command-line interface for Gadget\n\n {bold VERSION}\n ${config.versionFull}\n\n {bold USAGE}\n $ ggt [COMMAND]\n\n {bold FLAGS}\n -h, --help {gray Print command's usage}\n -v, --version {gray Print version}\n --debug {gray Print debug output}\n\n {bold COMMANDS}\n sync Sync your Gadget application's source code to and\n from your local filesystem.\n list List your apps.\n login Log in to your account.\n logout Log out of your account.\n whoami Print the currently logged in account.\n`;\n\nexport const rootArgsSpec = {\n \"--help\": Boolean,\n \"-h\": \"--help\",\n \"--version\": Boolean,\n \"-v\": \"--version\",\n \"--debug\": Boolean,\n};\n\nexport type RootArgs = arg.Result<typeof rootArgsSpec>;\n\nexport const run = async () => {\n await warnIfUpdateAvailable();\n\n const rootArgs = arg(rootArgsSpec, {\n argv: process.argv.slice(2),\n permissive: true,\n stopAtPositional: false,\n });\n\n if (rootArgs[\"--debug\"] ?? parseBoolean(process.env[\"DEBUG\"])) {\n debug.enable(\"ggt:*\");\n }\n\n if (rootArgs[\"--version\"]) {\n println(config.version);\n process.exit(0);\n }\n\n const command = rootArgs._.shift();\n if (_.isNil(command)) {\n println(usage);\n process.exit(0);\n }\n\n if (!_.includes(availableCommands, command)) {\n const [closest] = sortByLevenshtein(command, availableCommands);\n println`\n Unknown command {yellow ${command}}\n\n Did you mean {blueBright ${closest}}?\n\n Run {gray ggt --help} for usage\n `;\n process.exit(1);\n }\n\n const cmd: Command = await import(`./${command}.js`);\n\n if (rootArgs[\"--help\"]) {\n println(cmd.usage);\n process.exit(0);\n }\n\n try {\n await cmd.init?.(rootArgs);\n await cmd.run(rootArgs);\n } catch (cause) {\n const error = CLIError.from(cause);\n println(error.render());\n await error.capture();\n process.exit(1);\n }\n};\n"],"names":["arg","debug","_","parseBoolean","config","CLIError","println","sortByLevenshtein","sprint","warnIfUpdateAvailable","availableCommands","usage","versionFull","rootArgsSpec","Boolean","run","rootArgs","argv","process","slice","permissive","stopAtPositional","env","enable","version","exit","command","shift","isNil","includes","closest","cmd","init","cause","error","from","render","capture"],"mappings":"AAAA,OAAOA,SAAS,MAAM;AACtB,OAAOC,WAAW,QAAQ;AAC1B,OAAOC,OAAO,SAAS;AACvB,SAASC,YAAY,QAAQ,sBAAsB;AACnD,SAASC,MAAM,QAAQ,wBAAwB;AAC/C,SAASC,QAAQ,QAAQ,wBAAwB;AACjD,SAASC,OAAO,EAAEC,iBAAiB,EAAEC,MAAM,QAAQ,wBAAwB;AAC3E,SAASC,qBAAqB,QAAQ,yBAAyB;AAC/D,SAASC,iBAAiB,QAAsB,aAAa;AAE7D,OAAO,MAAMC,QAAQH,MAAM,CAAC;;;;MAItB,EAAEJ,OAAOQ,WAAW,CAAC;;;;;;;;;;;;;;;;;AAiB3B,CAAC,CAAC;AAEF,OAAO,MAAMC,eAAe;IAC1B,UAAUC;IACV,MAAM;IACN,aAAaA;IACb,MAAM;IACN,WAAWA;AACb,EAAE;AAIF,OAAO,MAAMC,MAAM;IACjB,MAAMN;IAEN,MAAMO,WAAWhB,IAAIa,cAAc;QACjCI,MAAMC,QAAQD,IAAI,CAACE,KAAK,CAAC;QACzBC,YAAY;QACZC,kBAAkB;IACpB;IAEA,IAAIL,QAAQ,CAAC,UAAU,IAAIb,aAAae,QAAQI,GAAG,CAAC,QAAQ,GAAG;QAC7DrB,MAAMsB,MAAM,CAAC;IACf;IAEA,IAAIP,QAAQ,CAAC,YAAY,EAAE;QACzBV,QAAQF,OAAOoB,OAAO;QACtBN,QAAQO,IAAI,CAAC;IACf;IAEA,MAAMC,UAAUV,SAASd,CAAC,CAACyB,KAAK;IAChC,IAAIzB,EAAE0B,KAAK,CAACF,UAAU;QACpBpB,QAAQK;QACRO,QAAQO,IAAI,CAAC;IACf;IAEA,IAAI,CAACvB,EAAE2B,QAAQ,CAACnB,mBAAmBgB,UAAU;QAC3C,MAAM,CAACI,QAAQ,GAAGvB,kBAAkBmB,SAAShB;QAC7CJ,OAAO,CAAC;8BACkB,EAAEoB,QAAQ;;+BAET,EAAEI,QAAQ;;;IAGrC,CAAC;QACDZ,QAAQO,IAAI,CAAC;IACf;IAEA,MAAMM,MAAe,MAAM,MAAM,CAAC,CAAC,EAAE,EAAEL,QAAQ,GAAG,CAAC;IAEnD,IAAIV,QAAQ,CAAC,SAAS,EAAE;QACtBV,QAAQyB,IAAIpB,KAAK;QACjBO,QAAQO,IAAI,CAAC;IACf;IAEA,IAAI;QACF,MAAMM,IAAIC,IAAI,GAAGhB;QACjB,MAAMe,IAAIhB,GAAG,CAACC;IAChB,EAAE,OAAOiB,OAAO;QACd,MAAMC,QAAQ7B,SAAS8B,IAAI,CAACF;QAC5B3B,QAAQ4B,MAAME,MAAM;QACpB,MAAMF,MAAMG,OAAO;QACnBnB,QAAQO,IAAI,CAAC;IACf;AACF,EAAE"}
1
+ {"version":3,"sources":["../../src/commands/root.ts"],"sourcesContent":["import arg from \"arg\";\nimport debug from \"debug\";\nimport { parseBoolean } from \"../services/args.js\";\nimport { config } from \"../services/config.js\";\nimport { CLIError } from \"../services/errors.js\";\nimport { isNil } from \"../services/is.js\";\nimport { println, sortByLevenshtein, sprint } from \"../services/output.js\";\nimport { warnIfUpdateAvailable } from \"../services/version.js\";\nimport { availableCommands, type Command } from \"./index.js\";\n\nexport const usage = sprint`\n The command-line interface for Gadget\n\n {bold VERSION}\n ${config.versionFull}\n\n {bold USAGE}\n $ ggt [COMMAND]\n\n {bold FLAGS}\n -h, --help {gray Print command's usage}\n -v, --version {gray Print version}\n --debug {gray Print debug output}\n\n {bold COMMANDS}\n sync Sync your Gadget application's source code to and\n from your local filesystem.\n list List your apps.\n login Log in to your account.\n logout Log out of your account.\n whoami Print the currently logged in account.\n`;\n\nexport const rootArgsSpec = {\n \"--help\": Boolean,\n \"-h\": \"--help\",\n \"--version\": Boolean,\n \"-v\": \"--version\",\n \"--debug\": Boolean,\n};\n\nexport type RootArgs = arg.Result<typeof rootArgsSpec>;\n\nexport const run = async () => {\n await warnIfUpdateAvailable();\n\n const rootArgs = arg(rootArgsSpec, {\n argv: process.argv.slice(2),\n permissive: true,\n stopAtPositional: false,\n });\n\n if (rootArgs[\"--debug\"] ?? parseBoolean(process.env[\"DEBUG\"])) {\n debug.enable(\"ggt:*\");\n }\n\n if (rootArgs[\"--version\"]) {\n println(config.version);\n process.exit(0);\n }\n\n const command = rootArgs._.shift() as (typeof availableCommands)[number] | undefined;\n if (isNil(command)) {\n println(usage);\n process.exit(0);\n }\n\n if (!availableCommands.includes(command)) {\n const [closest] = sortByLevenshtein(command, availableCommands);\n println`\n Unknown command {yellow ${command}}\n\n Did you mean {blueBright ${closest}}?\n\n Run {gray ggt --help} for usage\n `;\n process.exit(1);\n }\n\n const cmd = (await import(`./${command}.js`)) as Command;\n\n if (rootArgs[\"--help\"]) {\n println(cmd.usage);\n process.exit(0);\n }\n\n try {\n await cmd.init?.(rootArgs);\n await cmd.run(rootArgs);\n } catch (cause) {\n const error = CLIError.from(cause);\n println(error.render());\n await error.capture();\n process.exit(1);\n }\n};\n"],"names":["arg","debug","parseBoolean","config","CLIError","isNil","println","sortByLevenshtein","sprint","warnIfUpdateAvailable","availableCommands","usage","versionFull","rootArgsSpec","Boolean","run","rootArgs","argv","process","slice","permissive","stopAtPositional","env","enable","version","exit","command","_","shift","includes","closest","cmd","init","cause","error","from","render","capture"],"mappings":"AAAA,OAAOA,SAAS,MAAM;AACtB,OAAOC,WAAW,QAAQ;AAC1B,SAASC,YAAY,QAAQ,sBAAsB;AACnD,SAASC,MAAM,QAAQ,wBAAwB;AAC/C,SAASC,QAAQ,QAAQ,wBAAwB;AACjD,SAASC,KAAK,QAAQ,oBAAoB;AAC1C,SAASC,OAAO,EAAEC,iBAAiB,EAAEC,MAAM,QAAQ,wBAAwB;AAC3E,SAASC,qBAAqB,QAAQ,yBAAyB;AAC/D,SAASC,iBAAiB,QAAsB,aAAa;AAE7D,OAAO,MAAMC,QAAQH,MAAM,CAAC;;;;MAItB,EAAEL,OAAOS,WAAW,CAAC;;;;;;;;;;;;;;;;;AAiB3B,CAAC,CAAC;AAEF,OAAO,MAAMC,eAAe;IAC1B,UAAUC;IACV,MAAM;IACN,aAAaA;IACb,MAAM;IACN,WAAWA;AACb,EAAE;AAIF,OAAO,MAAMC,MAAM;IACjB,MAAMN;IAEN,MAAMO,WAAWhB,IAAIa,cAAc;QACjCI,MAAMC,QAAQD,IAAI,CAACE,KAAK,CAAC;QACzBC,YAAY;QACZC,kBAAkB;IACpB;IAEA,IAAIL,QAAQ,CAAC,UAAU,IAAId,aAAagB,QAAQI,GAAG,CAAC,QAAQ,GAAG;QAC7DrB,MAAMsB,MAAM,CAAC;IACf;IAEA,IAAIP,QAAQ,CAAC,YAAY,EAAE;QACzBV,QAAQH,OAAOqB,OAAO;QACtBN,QAAQO,IAAI,CAAC;IACf;IAEA,MAAMC,UAAUV,SAASW,CAAC,CAACC,KAAK;IAChC,IAAIvB,MAAMqB,UAAU;QAClBpB,QAAQK;QACRO,QAAQO,IAAI,CAAC;IACf;IAEA,IAAI,CAACf,kBAAkBmB,QAAQ,CAACH,UAAU;QACxC,MAAM,CAACI,QAAQ,GAAGvB,kBAAkBmB,SAAShB;QAC7CJ,OAAO,CAAC;8BACkB,EAAEoB,QAAQ;;+BAET,EAAEI,QAAQ;;;IAGrC,CAAC;QACDZ,QAAQO,IAAI,CAAC;IACf;IAEA,MAAMM,MAAO,MAAM,MAAM,CAAC,CAAC,EAAE,EAAEL,QAAQ,GAAG,CAAC;IAE3C,IAAIV,QAAQ,CAAC,SAAS,EAAE;QACtBV,QAAQyB,IAAIpB,KAAK;QACjBO,QAAQO,IAAI,CAAC;IACf;IAEA,IAAI;QACF,MAAMM,IAAIC,IAAI,GAAGhB;QACjB,MAAMe,IAAIhB,GAAG,CAACC;IAChB,EAAE,OAAOiB,OAAO;QACd,MAAMC,QAAQ9B,SAAS+B,IAAI,CAACF;QAC5B3B,QAAQ4B,MAAME,MAAM;QACpB,MAAMF,MAAMG,OAAO;QACnBnB,QAAQO,IAAI,CAAC;IACf;AACF,EAAE"}
@@ -1,27 +1,29 @@
1
1
  import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
2
  import arg from "arg";
3
- import { format as formatDate, isAfter } from "date-fns";
3
+ import dayjs from "dayjs";
4
4
  import { execa } from "execa";
5
5
  import fs from "fs-extra";
6
- import inquirer from "inquirer";
7
- import _ from "lodash";
8
6
  import ms from "ms";
7
+ import path from "node:path";
9
8
  import pMap from "p-map";
10
9
  import PQueue from "p-queue";
11
- import path from "path";
12
10
  import FSWatcher from "watcher";
13
11
  import which from "which";
14
12
  import { FileSyncEncoding } from "../__generated__/graphql.js";
15
13
  import { AppArg } from "../services/args.js";
16
14
  import { config } from "../services/config.js";
15
+ import { debounce } from "../services/debounce.js";
16
+ import { defaults } from "../services/defaults.js";
17
17
  import { EditGraphQL } from "../services/edit-graphql.js";
18
18
  import { YarnNotFoundError } from "../services/errors.js";
19
19
  import { FileSync, PUBLISH_FILE_SYNC_EVENTS_MUTATION, REMOTE_FILES_VERSION_QUERY, REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION, printPaths } from "../services/filesync.js";
20
- import { swallowEnoent } from "../services/fs-utils.js";
20
+ import { swallowEnoent } from "../services/fs.js";
21
21
  import { createLogger } from "../services/log.js";
22
+ import { noop } from "../services/noop.js";
22
23
  import { notify } from "../services/notify.js";
23
24
  import { println, sprint } from "../services/output.js";
24
25
  import { PromiseSignal } from "../services/promise.js";
26
+ import { select } from "../services/prompt.js";
25
27
  import { getUserOrLogin } from "../services/user.js";
26
28
  export const usage = sprint`
27
29
  Sync your Gadget application's source code to and from
@@ -133,7 +135,7 @@ export class Sync {
133
135
  * - Ensures yarn v1 is installed.
134
136
  * - Prompts the user how to resolve conflicts if the local filesystem has changed since the last sync.
135
137
  */ async init(rootArgs) {
136
- this.args = _.defaults(arg(argSpec, {
138
+ this.args = defaults(arg(argSpec, {
137
139
  argv: rootArgs._
138
140
  }), {
139
141
  "--file-push-delay": 100,
@@ -189,16 +191,14 @@ export class Sync {
189
191
  }
190
192
  let action;
191
193
  if (hasLocalChanges) {
192
- ({ action } = await inquirer.prompt({
193
- type: "list",
194
- name: "action",
194
+ action = await select({
195
+ message: hasRemoteChanges ? "Remote files have also changed. How would you like to proceed?" : "How would you like to proceed?",
195
196
  choices: [
196
197
  "Cancel (Ctrl+C)",
197
198
  "Merge local files with remote ones",
198
199
  "Reset local files to remote ones"
199
- ],
200
- message: hasRemoteChanges ? "Remote files have also changed. How would you like to proceed?" : "How would you like to proceed?"
201
- }));
200
+ ]
201
+ });
202
202
  }
203
203
  // get all the changed files again in case more changed
204
204
  changedFiles = await getChangedFiles();
@@ -261,14 +261,16 @@ export class Sync {
261
261
  const stopped = new PromiseSignal();
262
262
  const recentRemoteChangesInterval = setInterval(()=>{
263
263
  for (const [path, timestamp] of this.recentRemoteChanges){
264
- if (isAfter(Date.now(), timestamp + ms("5s"))) {
264
+ if (dayjs().isAfter(timestamp + ms("5s"))) {
265
265
  // this change should have been seen by now, so remove it
266
266
  this.recentRemoteChanges.delete(path);
267
267
  }
268
268
  }
269
269
  }, ms("1s")).unref();
270
270
  this.stop = async (e)=>{
271
- if (this.status != 1) return;
271
+ if (this.status !== 1) {
272
+ return;
273
+ }
272
274
  this.status = 2;
273
275
  error = e;
274
276
  this.log.info("stopping", {
@@ -295,7 +297,9 @@ export class Sync {
295
297
  "SIGTERM"
296
298
  ]){
297
299
  process.on(signal, ()=>{
298
- if (this.status != 1) return;
300
+ if (this.status !== 1) {
301
+ return;
302
+ }
299
303
  println` Stopping... {gray (press Ctrl+C again to force)}`;
300
304
  void this.stop();
301
305
  // When ggt is run via npx, and the user presses Ctrl+C, npx sends SIGINT twice in quick succession. In order to prevent the second
@@ -318,22 +322,22 @@ export class Sync {
318
322
  next: ({ remoteFileSyncEvents })=>{
319
323
  const remoteFilesVersion = remoteFileSyncEvents.remoteFilesVersion;
320
324
  // we always ignore .gadget/ files so that we don't publish them (they're managed by gadget), but we still want to receive them
321
- const filter = (event)=>_.startsWith(event.path, ".gadget/") || !this.filesync.ignores(event.path);
322
- const changed = _.filter(remoteFileSyncEvents.changed, filter);
323
- const deleted = _.filter(remoteFileSyncEvents.deleted, filter);
325
+ const filterIgnored = (event)=>event.path.startsWith(".gadget/") || !this.filesync.ignores(event.path);
326
+ const changed = remoteFileSyncEvents.changed.filter(filterIgnored);
327
+ const deleted = remoteFileSyncEvents.deleted.filter(filterIgnored);
324
328
  this.log.info("received files", {
325
329
  remoteFilesVersion,
326
- changed: _.map(changed, "path"),
327
- deleted: _.map(deleted, "path")
330
+ changed: changed.map((x)=>x.path),
331
+ deleted: deleted.map((x)=>x.path)
328
332
  });
329
333
  this._enqueue(async ()=>{
330
334
  // add all the non-ignored files and directories we're about
331
335
  // to touch to recentRemoteChanges so that we don't send
332
336
  // them back
333
- for (const file of _.filter([
337
+ for (const file of [
334
338
  ...changed,
335
339
  ...deleted
336
- ], (file)=>!this.filesync.ignores(file.path))){
340
+ ].filter((file)=>!this.filesync.ignores(file.path))){
337
341
  this.recentRemoteChanges.set(file.path, Date.now());
338
342
  let dir = path.dirname(file.path);
339
343
  while(dir !== "."){
@@ -341,26 +345,23 @@ export class Sync {
341
345
  dir = path.dirname(dir);
342
346
  }
343
347
  }
344
- if (changed.length || deleted.length) {
345
- println`Received {gray ${formatDate(new Date(), "pp")}}`;
346
- printPaths("←", _.map(changed, "path"), _.map(deleted, "path"));
348
+ if (changed.length > 0 || deleted.length > 0) {
349
+ println`Received {gray ${dayjs().format("hh:mm:ss A")}}`;
350
+ printPaths("←", changed.map((x)=>x.path), deleted.map((x)=>x.path));
347
351
  }
348
- await this.filesync.write(remoteFilesVersion, changed, _.map(deleted, "path"));
349
- if (_.some(changed, [
350
- "path",
351
- "yarn.lock"
352
- ])) {
352
+ await this.filesync.write(remoteFilesVersion, changed, deleted.map((x)=>x.path));
353
+ if (changed.some((x)=>x.path === "yarn.lock")) {
353
354
  await execa("yarn", [
354
355
  "install"
355
356
  ], {
356
357
  cwd: this.filesync.dir
357
- }).catch(_.noop);
358
+ }).catch(noop);
358
359
  }
359
360
  });
360
361
  }
361
362
  });
362
363
  const localFilesBuffer = new Map();
363
- this.publish = _.debounce(()=>{
364
+ this.publish = debounce(this.args["--file-push-delay"], ()=>{
364
365
  const localFiles = new Map(localFilesBuffer.entries());
365
366
  localFilesBuffer.clear();
366
367
  this._enqueue(async ()=>{
@@ -387,7 +388,7 @@ export class Sync {
387
388
  swallowEnoent(error);
388
389
  }
389
390
  });
390
- if (!changed.length && !deleted.length) {
391
+ if (changed.length === 0 && deleted.length === 0) {
391
392
  return;
392
393
  }
393
394
  const { publishFileSyncEvents } = await this.graphql.query({
@@ -401,15 +402,14 @@ export class Sync {
401
402
  }
402
403
  });
403
404
  await this.filesync.write(publishFileSyncEvents.remoteFilesVersion, [], []);
404
- println`Sent {gray ${formatDate(new Date(), "pp")}}`;
405
- printPaths("→", _.map(changed, "path"), _.map(deleted, "path"));
405
+ println`Sent {gray ${dayjs().format("hh:mm:ss A")}}`;
406
+ printPaths("→", changed.map((x)=>x.path), deleted.map((x)=>x.path));
406
407
  });
407
- }, this.args["--file-push-delay"]);
408
+ });
408
409
  this.watcher = new FSWatcher(this.filesync.dir, {
409
- // paths that we never want to publish
410
- ignore: /(\.gadget|\.git|node_modules|\.DS_Store)/,
411
410
  // don't emit an event for every watched file on boot
412
411
  ignoreInitial: true,
412
+ ignore: (path)=>this.filesync.ignores(path),
413
413
  renameDetection: true,
414
414
  recursive: true,
415
415
  debounce: this.args["--file-watch-debounce"],
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/commands/sync.ts"],"sourcesContent":["import arg from \"arg\";\nimport { format as formatDate, isAfter } from \"date-fns\";\nimport { execa } from \"execa\";\nimport type { Stats } from \"fs-extra\";\nimport fs from \"fs-extra\";\nimport inquirer from \"inquirer\";\nimport _ from \"lodash\";\nimport ms from \"ms\";\nimport pMap from \"p-map\";\nimport PQueue from \"p-queue\";\nimport path from \"path\";\nimport FSWatcher from \"watcher\";\nimport which from \"which\";\nimport { FileSyncEncoding, type FileSyncChangedEventInput, type FileSyncDeletedEventInput } from \"../__generated__/graphql.js\";\nimport { AppArg } from \"../services/args.js\";\nimport { config } from \"../services/config.js\";\nimport { EditGraphQL } from \"../services/edit-graphql.js\";\nimport { YarnNotFoundError } from \"../services/errors.js\";\nimport {\n FileSync,\n PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n REMOTE_FILES_VERSION_QUERY,\n REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n printPaths,\n} from \"../services/filesync.js\";\nimport { swallowEnoent } from \"../services/fs-utils.js\";\nimport { createLogger } from \"../services/log.js\";\nimport { notify } from \"../services/notify.js\";\nimport { println, sprint } from \"../services/output.js\";\nimport { PromiseSignal } from \"../services/promise.js\";\nimport { getUserOrLogin } from \"../services/user.js\";\nimport { type RootArgs } from \"./root.js\";\n\nexport const usage = sprint`\n Sync your Gadget application's source code to and from\n your local filesystem.\n\n {bold USAGE}\n $ ggt sync [DIRECTORY] [--app <name>]\n\n {bold ARGUMENTS}\n DIRECTORY {dim [default: .] The directory to sync files to.\n\n If the directory doesn't exist, it will be created.}\n\n {bold FLAGS}\n -a, --app=<name> {dim The Gadget application to sync files to.}\n\n --force {dim Whether to sync even if we can't determine\n the state of your local files relative to\n your remote ones.}\n\n {bold DESCRIPTION}\n Sync provides the ability to sync your Gadget application's source\n code to and from your local filesystem.\n\n While ggt sync is running, local file changes are immediately\n reflected within Gadget, while files that are changed remotely are\n immediately saved to your local filesystem.\n\n Use cases for this include:\n • Developing locally with your own editor like VSCode\n • Storing your source code in a Git repository like GitHub\n\n Sync includes the concept of a {dim .ignore} file. This file may\n contain a list of files and directories that won't be received or\n sent to Gadget when syncing. The format of this file is identical\n to the one used by Git {dim (https://git-scm.com/docs/gitignore)}.\n\n The following files and directories are always ignored:\n • .DS_Store\n • .gadget\n • .git\n • node_modules\n\n Note:\n • If you have separate development and production environments,\n {dim ggt sync} will only sync with your development environment\n • Gadget applications only support installing dependencies\n with Yarn 1 {dim (https://classic.yarnpkg.com/lang/en/)}\n • Since file changes are immediately reflected in Gadget,\n avoid the following while {dim ggt sync} is running:\n • Deleting all your files\n • Moving all your files to a different directory\n\n {bold EXAMPLES}\n {dim $ ggt sync --app my-app ~/gadget/my-app}\n\n App my-app\n Editor https://my-app.gadget.app/edit\n Playground https://my-app.gadget.app/api/graphql/playground\n Docs https://docs.gadget.dev/api/my-app\n\n Endpoints\n • https://my-app.gadget.app\n • https://my-app--development.gadget.app\n\n Watching for file changes... {dim Press Ctrl+C to stop}\n\n Received {dim 12:00:00 PM}\n {green ←} routes/GET.js {dim (changed)}\n {green ←} user/signUp/signIn.js {dim (changed)}\n {dim 2 files in total. 2 changed, 0 deleted.}\n\n Sent {dim 12:00:03 PM}\n {green →} routes/GET.ts {dim (changed)}\n {dim 1 file in total. 1 changed, 0 deleted.}\n\n ^C Stopping... {dim (press Ctrl+C again to force)}\n Goodbye!\n`;\n\nexport enum SyncStatus {\n STARTING,\n RUNNING,\n STOPPING,\n STOPPED,\n}\n\nexport enum Action {\n CANCEL = \"Cancel (Ctrl+C)\",\n MERGE = \"Merge local files with remote ones\",\n RESET = \"Reset local files to remote ones\",\n}\n\nconst argSpec = {\n \"-a\": \"--app\",\n \"--app\": AppArg,\n \"--force\": Boolean,\n \"--file-push-delay\": Number,\n \"--file-watch-debounce\": Number,\n \"--file-watch-poll-interval\": Number,\n \"--file-watch-poll-timeout\": Number,\n \"--file-watch-rename-timeout\": Number,\n};\n\nexport class Sync {\n args!: arg.Result<typeof argSpec>;\n\n /**\n * The current status of the sync process.\n */\n status = SyncStatus.STARTING;\n\n /**\n * A list of filepaths that have changed because of a remote file-sync\n * event. This is used to avoid sending files that we recently\n * received from a remote file-sync event.\n */\n recentRemoteChanges = new Map<string, number>();\n\n /**\n * A FIFO async callback queue that ensures we process file-sync events in the order they occurred.\n */\n queue = new PQueue({ concurrency: 1 });\n\n /**\n * A GraphQL client connected to the app's /edit/api/graphql-ws endpoint\n */\n graphql!: EditGraphQL;\n\n /**\n * Watches the local filesystem for changes.\n */\n watcher!: FSWatcher;\n\n /**\n * Handles writing files to the local filesystem.\n */\n filesync!: FileSync;\n\n /**\n * A debounced function that enqueue's local file changes to be sent to Gadget.\n */\n publish!: _.DebouncedFunc<() => void>;\n\n /**\n * Gracefully stops the sync.\n */\n stop!: (error?: unknown) => Promise<void>;\n\n /**\n * A logger for the sync command.\n */\n log = createLogger(\"sync\", () => {\n return {\n app: this.filesync.app.slug,\n filesVersion: String(this.filesync.filesVersion),\n mtime: this.filesync.mtime,\n };\n });\n\n /**\n * Initializes the sync process.\n * - Ensures the directory exists.\n * - Ensures the directory is empty or contains a `.gadget/sync.json` file.\n * - Ensures an app is selected and that it matches the app the directory was previously synced to.\n * - Ensures yarn v1 is installed.\n * - Prompts the user how to resolve conflicts if the local filesystem has changed since the last sync.\n */\n async init(rootArgs: RootArgs): Promise<void> {\n this.args = _.defaults(arg(argSpec, { argv: rootArgs._ }), {\n \"--file-push-delay\": 100,\n \"--file-watch-debounce\": 300,\n \"--file-watch-poll-interval\": 3_000,\n \"--file-watch-poll-timeout\": 20_000,\n \"--file-watch-rename-timeout\": 1_250,\n });\n\n if (!which.sync(\"yarn\", { nothrow: true })) {\n throw new YarnNotFoundError();\n }\n\n const user = await getUserOrLogin();\n\n this.filesync = await FileSync.init(user, {\n dir: this.args._[0],\n app: this.args[\"--app\"],\n force: this.args[\"--force\"],\n extraIgnorePaths: [\".gadget\"],\n });\n\n this.graphql = new EditGraphQL(this.filesync.app);\n\n const { remoteFilesVersion } = await this.graphql.query({ query: REMOTE_FILES_VERSION_QUERY });\n const hasRemoteChanges = BigInt(remoteFilesVersion) > this.filesync.filesVersion;\n\n const getChangedFiles = async (): Promise<Map<string, Stats>> => {\n const files = new Map();\n for await (const [absolutePath, stats] of this.filesync.walkDir()) {\n if (stats.mtime.getTime() > this.filesync.mtime) {\n files.set(this.filesync.normalize(absolutePath, stats.isDirectory()), stats);\n }\n }\n\n // never include the root directory\n files.delete(\"/\");\n\n return files;\n };\n\n let changedFiles = await getChangedFiles();\n const hasLocalChanges = changedFiles.size > 0;\n if (hasLocalChanges) {\n this.log.info(\"local files have changed\", {\n remoteFilesVersion,\n hasRemoteChanges,\n hasLocalChanges,\n changed: Array.from(changedFiles.keys()),\n });\n\n println(\"Local files have changed since you last synced\");\n printPaths(\"-\", Array.from(changedFiles.keys()), [], { limit: changedFiles.size });\n println();\n }\n\n let action: Action | undefined;\n if (hasLocalChanges) {\n ({ action } = await inquirer.prompt({\n type: \"list\",\n name: \"action\",\n choices: [Action.CANCEL, Action.MERGE, Action.RESET],\n message: hasRemoteChanges ? \"Remote files have also changed. How would you like to proceed?\" : \"How would you like to proceed?\",\n }));\n }\n\n // get all the changed files again in case more changed\n changedFiles = await getChangedFiles();\n\n switch (action) {\n case Action.MERGE: {\n this.log.info(\"merging local changes\", {\n remoteFilesVersion,\n hasRemoteChanges,\n hasLocalChanges,\n changed: Array.from(changedFiles.keys()),\n });\n\n // We purposefully don't write the returned files version here\n // because we haven't received its associated files yet. This\n // will cause us to receive the remote files that have changed\n // since the last sync (+ the local files that we just\n // published)\n await this.graphql.query({\n query: PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n variables: {\n input: {\n expectedRemoteFilesVersion: remoteFilesVersion,\n changed: await pMap(changedFiles, async ([normalizedPath, stats]) => ({\n path: normalizedPath,\n mode: stats.mode,\n content: stats.isDirectory() ? \"\" : await fs.readFile(this.filesync.absolute(normalizedPath), \"base64\"),\n encoding: FileSyncEncoding.Base64,\n })),\n deleted: [],\n },\n },\n });\n break;\n }\n case Action.RESET: {\n this.log.info(\"resetting local changes\", {\n remoteFilesVersion,\n hasRemoteChanges,\n hasLocalChanges,\n changed: Array.from(changedFiles.keys()),\n });\n\n // delete all the local files that have changed since the last\n // sync and set the files version to 0 so we receive all the\n // remote files again, including any files that we just deleted\n // that still exist\n await this.filesync.write(0n, [], changedFiles.keys(), true);\n break;\n }\n case Action.CANCEL: {\n process.exit(0);\n }\n }\n }\n\n /**\n * Runs the sync process until it is stopped or an error occurs.\n */\n async run(): Promise<void> {\n let error: unknown;\n const stopped = new PromiseSignal();\n\n const recentRemoteChangesInterval = setInterval(() => {\n for (const [path, timestamp] of this.recentRemoteChanges) {\n if (isAfter(Date.now(), timestamp + ms(\"5s\"))) {\n // this change should have been seen by now, so remove it\n this.recentRemoteChanges.delete(path);\n }\n }\n }, ms(\"1s\")).unref();\n\n this.stop = async (e?: unknown) => {\n if (this.status != SyncStatus.RUNNING) return;\n this.status = SyncStatus.STOPPING;\n error = e;\n\n this.log.info(\"stopping\", { error });\n\n try {\n clearInterval(recentRemoteChangesInterval);\n unsubscribe();\n this.watcher.removeAllListeners();\n this.publish.flush();\n await this.queue.onIdle();\n } finally {\n await Promise.allSettled([this.watcher.close(), this.graphql.dispose()]);\n\n this.status = SyncStatus.STOPPED;\n stopped.resolve();\n this.log.info(\"stopped\");\n }\n };\n\n for (const signal of [\"SIGINT\", \"SIGTERM\"] as const) {\n process.on(signal, () => {\n if (this.status != SyncStatus.RUNNING) return;\n\n println` Stopping... {gray (press Ctrl+C again to force)}`;\n void this.stop();\n\n // When ggt is run via npx, and the user presses Ctrl+C, npx sends SIGINT twice in quick succession. In order to prevent the second\n // SIGINT from triggering the force exit listener, we wait a bit before registering it. This is a bit of a hack, but it works.\n setTimeout(() => {\n process.once(signal, () => {\n println(\" Exiting immediately. Note that files may not have finished syncing.\");\n process.exit(1);\n });\n }, 100).unref();\n });\n }\n\n const unsubscribe = this.graphql.subscribe(\n {\n query: REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n variables: () => ({ localFilesVersion: String(this.filesync.filesVersion) }),\n },\n {\n error: (error) => void this.stop(error),\n next: ({ remoteFileSyncEvents }) => {\n const remoteFilesVersion = remoteFileSyncEvents.remoteFilesVersion;\n\n // we always ignore .gadget/ files so that we don't publish them (they're managed by gadget), but we still want to receive them\n const filter = (event: { path: string }) => _.startsWith(event.path, \".gadget/\") || !this.filesync.ignores(event.path);\n const changed = _.filter(remoteFileSyncEvents.changed, filter);\n const deleted = _.filter(remoteFileSyncEvents.deleted, filter);\n\n this.log.info(\"received files\", {\n remoteFilesVersion,\n changed: _.map(changed, \"path\"),\n deleted: _.map(deleted, \"path\"),\n });\n\n this._enqueue(async () => {\n // add all the non-ignored files and directories we're about\n // to touch to recentRemoteChanges so that we don't send\n // them back\n for (const file of _.filter([...changed, ...deleted], (file) => !this.filesync.ignores(file.path))) {\n this.recentRemoteChanges.set(file.path, Date.now());\n\n let dir = path.dirname(file.path);\n while (dir !== \".\") {\n this.recentRemoteChanges.set(dir + \"/\", Date.now());\n dir = path.dirname(dir);\n }\n }\n\n if (changed.length || deleted.length) {\n println`Received {gray ${formatDate(new Date(), \"pp\")}}`;\n printPaths(\"←\", _.map(changed, \"path\"), _.map(deleted, \"path\"));\n }\n\n await this.filesync.write(remoteFilesVersion, changed, _.map(deleted, \"path\"));\n\n if (_.some(changed, [\"path\", \"yarn.lock\"])) {\n await execa(\"yarn\", [\"install\"], { cwd: this.filesync.dir }).catch(_.noop);\n }\n });\n },\n },\n );\n\n const localFilesBuffer = new Map<\n string,\n | { mode: number; isDirectory: boolean }\n | { isDeleted: true; isDirectory: boolean }\n | { mode: number; oldPath: string; newPath: string; isDirectory: boolean }\n >();\n\n this.publish = _.debounce(() => {\n const localFiles = new Map(localFilesBuffer.entries());\n localFilesBuffer.clear();\n\n this._enqueue(async () => {\n const changed: FileSyncChangedEventInput[] = [];\n const deleted: FileSyncDeletedEventInput[] = [];\n\n await pMap(localFiles, async ([normalizedPath, file]) => {\n if (\"isDeleted\" in file) {\n deleted.push({ path: normalizedPath });\n return;\n }\n\n try {\n changed.push({\n path: normalizedPath,\n oldPath: \"oldPath\" in file ? file.oldPath : undefined,\n mode: file.mode,\n content: file.isDirectory ? \"\" : await fs.readFile(this.filesync.absolute(normalizedPath), FileSyncEncoding.Base64),\n encoding: FileSyncEncoding.Base64,\n });\n } catch (error) {\n // A file could have been changed and then deleted before we process the change event, so the readFile\n // above will raise an ENOENT. This is normal operation, so just ignore this event.\n swallowEnoent(error);\n }\n });\n\n if (!changed.length && !deleted.length) {\n return;\n }\n\n const { publishFileSyncEvents } = await this.graphql.query({\n query: PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n variables: { input: { expectedRemoteFilesVersion: String(this.filesync.filesVersion), changed, deleted } },\n });\n\n await this.filesync.write(publishFileSyncEvents.remoteFilesVersion, [], []);\n\n println`Sent {gray ${formatDate(new Date(), \"pp\")}}`;\n printPaths(\"→\", _.map(changed, \"path\"), _.map(deleted, \"path\"));\n });\n }, this.args[\"--file-push-delay\"]);\n\n this.watcher = new FSWatcher(this.filesync.dir, {\n // paths that we never want to publish\n ignore: /(\\.gadget|\\.git|node_modules|\\.DS_Store)/,\n // don't emit an event for every watched file on boot\n ignoreInitial: true,\n renameDetection: true,\n recursive: true,\n debounce: this.args[\"--file-watch-debounce\"],\n pollingInterval: this.args[\"--file-watch-poll-interval\"],\n pollingTimeout: this.args[\"--file-watch-poll-timeout\"],\n renameTimeout: this.args[\"--file-watch-rename-timeout\"],\n });\n\n this.watcher.once(\"error\", (error) => void this.stop(error));\n\n this.watcher.on(\"all\", (event: string, absolutePath: string, renamedPath: string) => {\n const filepath = event === \"rename\" || event === \"renameDir\" ? renamedPath : absolutePath;\n const isDirectory = event === \"renameDir\" || event === \"addDir\" || event === \"unlinkDir\";\n const normalizedPath = this.filesync.normalize(filepath, isDirectory);\n\n this.log.debug(\"file event\", {\n event,\n path: normalizedPath,\n isDirectory,\n recentRemoteChanges: Array.from(this.recentRemoteChanges.keys()),\n });\n\n if (filepath === this.filesync.absolute(\".ignore\")) {\n this.filesync.reloadIgnorePaths();\n } else if (this.filesync.ignores(filepath)) {\n return;\n }\n\n if (this.recentRemoteChanges.delete(normalizedPath)) {\n return;\n }\n\n switch (event) {\n case \"add\":\n case \"addDir\":\n case \"change\": {\n const stats = fs.statSync(filepath);\n localFilesBuffer.set(normalizedPath, { mode: stats.mode, isDirectory });\n break;\n }\n case \"unlink\":\n case \"unlinkDir\": {\n localFilesBuffer.set(normalizedPath, { isDeleted: true, isDirectory });\n break;\n }\n case \"rename\":\n case \"renameDir\": {\n const stats = fs.statSync(filepath);\n localFilesBuffer.set(normalizedPath, {\n oldPath: this.filesync.normalize(absolutePath, isDirectory),\n newPath: normalizedPath,\n isDirectory,\n mode: stats.mode,\n });\n break;\n }\n }\n\n this.publish();\n });\n\n this.status = SyncStatus.RUNNING;\n\n println();\n println`\n {bold ggt v${config.version}}\n\n App ${this.filesync.app.slug}\n Editor https://${this.filesync.app.slug}.gadget.app/edit\n Playground https://${this.filesync.app.slug}.gadget.app/api/graphql/playground\n Docs https://docs.gadget.dev/api/${this.filesync.app.slug}\n\n {underline Endpoints} ${\n this.filesync.app.hasSplitEnvironments\n ? `\n • https://${this.filesync.app.primaryDomain}\n • https://${this.filesync.app.slug}--development.gadget.app`\n : `\n • https://${this.filesync.app.primaryDomain}`\n }\n\n Watching for file changes... {gray Press Ctrl+C to stop}\n `;\n println();\n\n await stopped;\n\n if (error) {\n notify({ subtitle: \"Uh oh!\", message: \"An error occurred while syncing files\" });\n throw error as Error;\n } else {\n println(\"Goodbye!\");\n }\n }\n\n /**\n * Enqueues a function that handles file-sync events onto the {@linkcode queue}.\n *\n * @param fn The function to enqueue.\n */\n private _enqueue(fn: () => Promise<unknown>): void {\n void this.queue.add(fn).catch(this.stop);\n }\n}\n\nconst sync = new Sync();\nexport const init = sync.init.bind(sync);\nexport const run = sync.run.bind(sync);\n"],"names":["arg","format","formatDate","isAfter","execa","fs","inquirer","_","ms","pMap","PQueue","path","FSWatcher","which","FileSyncEncoding","AppArg","config","EditGraphQL","YarnNotFoundError","FileSync","PUBLISH_FILE_SYNC_EVENTS_MUTATION","REMOTE_FILES_VERSION_QUERY","REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION","printPaths","swallowEnoent","createLogger","notify","println","sprint","PromiseSignal","getUserOrLogin","usage","SyncStatus","Action","argSpec","Boolean","Number","Sync","init","rootArgs","args","defaults","argv","sync","nothrow","user","filesync","dir","app","force","extraIgnorePaths","graphql","remoteFilesVersion","query","hasRemoteChanges","BigInt","filesVersion","getChangedFiles","files","Map","absolutePath","stats","walkDir","mtime","getTime","set","normalize","isDirectory","delete","changedFiles","hasLocalChanges","size","log","info","changed","Array","from","keys","limit","action","prompt","type","name","choices","message","variables","input","expectedRemoteFilesVersion","normalizedPath","mode","content","readFile","absolute","encoding","Base64","deleted","write","process","exit","run","error","stopped","recentRemoteChangesInterval","setInterval","timestamp","recentRemoteChanges","Date","now","unref","stop","e","status","clearInterval","unsubscribe","watcher","removeAllListeners","publish","flush","queue","onIdle","Promise","allSettled","close","dispose","resolve","signal","on","setTimeout","once","subscribe","localFilesVersion","String","next","remoteFileSyncEvents","filter","event","startsWith","ignores","map","_enqueue","file","dirname","length","some","cwd","catch","noop","localFilesBuffer","debounce","localFiles","entries","clear","push","oldPath","undefined","publishFileSyncEvents","ignore","ignoreInitial","renameDetection","recursive","pollingInterval","pollingTimeout","renameTimeout","renamedPath","filepath","debug","reloadIgnorePaths","statSync","isDeleted","newPath","version","slug","hasSplitEnvironments","primaryDomain","subtitle","fn","add","concurrency","bind"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/commands/sync.ts"],"sourcesContent":["import arg from \"arg\";\nimport dayjs from \"dayjs\";\nimport { execa } from \"execa\";\nimport type { Stats } from \"fs-extra\";\nimport fs from \"fs-extra\";\nimport ms from \"ms\";\nimport path from \"node:path\";\nimport pMap from \"p-map\";\nimport PQueue from \"p-queue\";\nimport type { SetRequired } from \"type-fest\";\nimport FSWatcher from \"watcher\";\nimport which from \"which\";\nimport { FileSyncEncoding, type FileSyncChangedEventInput, type FileSyncDeletedEventInput } from \"../__generated__/graphql.js\";\nimport { AppArg } from \"../services/args.js\";\nimport { config } from \"../services/config.js\";\nimport { debounce, type DebouncedFunc } from \"../services/debounce.js\";\nimport { defaults } from \"../services/defaults.js\";\nimport { EditGraphQL } from \"../services/edit-graphql.js\";\nimport { YarnNotFoundError } from \"../services/errors.js\";\nimport {\n FileSync,\n PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n REMOTE_FILES_VERSION_QUERY,\n REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n printPaths,\n} from \"../services/filesync.js\";\nimport { swallowEnoent } from \"../services/fs.js\";\nimport { createLogger } from \"../services/log.js\";\nimport { noop } from \"../services/noop.js\";\nimport { notify } from \"../services/notify.js\";\nimport { println, sprint } from \"../services/output.js\";\nimport { PromiseSignal } from \"../services/promise.js\";\nimport { select } from \"../services/prompt.js\";\nimport { getUserOrLogin } from \"../services/user.js\";\nimport { type RootArgs } from \"./root.js\";\n\nexport const usage = sprint`\n Sync your Gadget application's source code to and from\n your local filesystem.\n\n {bold USAGE}\n $ ggt sync [DIRECTORY] [--app <name>]\n\n {bold ARGUMENTS}\n DIRECTORY {dim [default: .] The directory to sync files to.\n\n If the directory doesn't exist, it will be created.}\n\n {bold FLAGS}\n -a, --app=<name> {dim The Gadget application to sync files to.}\n\n --force {dim Whether to sync even if we can't determine\n the state of your local files relative to\n your remote ones.}\n\n {bold DESCRIPTION}\n Sync provides the ability to sync your Gadget application's source\n code to and from your local filesystem.\n\n While ggt sync is running, local file changes are immediately\n reflected within Gadget, while files that are changed remotely are\n immediately saved to your local filesystem.\n\n Use cases for this include:\n • Developing locally with your own editor like VSCode\n • Storing your source code in a Git repository like GitHub\n\n Sync includes the concept of a {dim .ignore} file. This file may\n contain a list of files and directories that won't be received or\n sent to Gadget when syncing. The format of this file is identical\n to the one used by Git {dim (https://git-scm.com/docs/gitignore)}.\n\n The following files and directories are always ignored:\n • .DS_Store\n • .gadget\n • .git\n • node_modules\n\n Note:\n • If you have separate development and production environments,\n {dim ggt sync} will only sync with your development environment\n • Gadget applications only support installing dependencies\n with Yarn 1 {dim (https://classic.yarnpkg.com/lang/en/)}\n • Since file changes are immediately reflected in Gadget,\n avoid the following while {dim ggt sync} is running:\n • Deleting all your files\n • Moving all your files to a different directory\n\n {bold EXAMPLES}\n {dim $ ggt sync --app my-app ~/gadget/my-app}\n\n App my-app\n Editor https://my-app.gadget.app/edit\n Playground https://my-app.gadget.app/api/graphql/playground\n Docs https://docs.gadget.dev/api/my-app\n\n Endpoints\n • https://my-app.gadget.app\n • https://my-app--development.gadget.app\n\n Watching for file changes... {dim Press Ctrl+C to stop}\n\n Received {dim 12:00:00 PM}\n {green ←} routes/GET.js {dim (changed)}\n {green ←} user/signUp/signIn.js {dim (changed)}\n {dim 2 files in total. 2 changed, 0 deleted.}\n\n Sent {dim 12:00:03 PM}\n {green →} routes/GET.ts {dim (changed)}\n {dim 1 file in total. 1 changed, 0 deleted.}\n\n ^C Stopping... {dim (press Ctrl+C again to force)}\n Goodbye!\n`;\n\nexport enum SyncStatus {\n STARTING,\n RUNNING,\n STOPPING,\n STOPPED,\n}\n\nexport enum Action {\n CANCEL = \"Cancel (Ctrl+C)\",\n MERGE = \"Merge local files with remote ones\",\n RESET = \"Reset local files to remote ones\",\n}\n\nconst argSpec = {\n \"-a\": \"--app\",\n \"--app\": AppArg,\n \"--force\": Boolean,\n \"--file-push-delay\": Number,\n \"--file-watch-debounce\": Number,\n \"--file-watch-poll-interval\": Number,\n \"--file-watch-poll-timeout\": Number,\n \"--file-watch-rename-timeout\": Number,\n};\n\nexport class Sync {\n args!: SetRequired<arg.Result<typeof argSpec>, \"--file-push-delay\">;\n\n /**\n * The current status of the sync process.\n */\n status = SyncStatus.STARTING;\n\n /**\n * A list of filepaths that have changed because of a remote file-sync\n * event. This is used to avoid sending files that we recently\n * received from a remote file-sync event.\n */\n recentRemoteChanges = new Map<string, number>();\n\n /**\n * A FIFO async callback queue that ensures we process file-sync events in the order they occurred.\n */\n queue = new PQueue({ concurrency: 1 });\n\n /**\n * A GraphQL client connected to the app's /edit/api/graphql-ws endpoint\n */\n graphql!: EditGraphQL;\n\n /**\n * Watches the local filesystem for changes.\n */\n watcher!: FSWatcher;\n\n /**\n * Handles writing files to the local filesystem.\n */\n filesync!: FileSync;\n\n /**\n * A debounced function that enqueue's local file changes to be sent to Gadget.\n */\n publish!: DebouncedFunc<() => void>;\n\n /**\n * Gracefully stops the sync.\n */\n stop!: (error?: unknown) => Promise<void>;\n\n /**\n * A logger for the sync command.\n */\n log = createLogger(\"sync\", () => {\n return {\n app: this.filesync.app.slug,\n filesVersion: String(this.filesync.filesVersion),\n mtime: this.filesync.mtime,\n };\n });\n\n /**\n * Initializes the sync process.\n * - Ensures the directory exists.\n * - Ensures the directory is empty or contains a `.gadget/sync.json` file.\n * - Ensures an app is selected and that it matches the app the directory was previously synced to.\n * - Ensures yarn v1 is installed.\n * - Prompts the user how to resolve conflicts if the local filesystem has changed since the last sync.\n */\n async init(rootArgs: RootArgs): Promise<void> {\n this.args = defaults(arg(argSpec, { argv: rootArgs._ }), {\n \"--file-push-delay\": 100,\n \"--file-watch-debounce\": 300,\n \"--file-watch-poll-interval\": 3_000,\n \"--file-watch-poll-timeout\": 20_000,\n \"--file-watch-rename-timeout\": 1_250,\n });\n\n if (!which.sync(\"yarn\", { nothrow: true })) {\n throw new YarnNotFoundError();\n }\n\n const user = await getUserOrLogin();\n\n this.filesync = await FileSync.init(user, {\n dir: this.args._[0],\n app: this.args[\"--app\"],\n force: this.args[\"--force\"],\n extraIgnorePaths: [\".gadget\"],\n });\n\n this.graphql = new EditGraphQL(this.filesync.app);\n\n const { remoteFilesVersion } = await this.graphql.query({ query: REMOTE_FILES_VERSION_QUERY });\n const hasRemoteChanges = BigInt(remoteFilesVersion) > this.filesync.filesVersion;\n\n const getChangedFiles = async (): Promise<Map<string, Stats>> => {\n const files = new Map();\n for await (const [absolutePath, stats] of this.filesync.walkDir()) {\n if (stats.mtime.getTime() > this.filesync.mtime) {\n files.set(this.filesync.normalize(absolutePath, stats.isDirectory()), stats);\n }\n }\n\n // never include the root directory\n files.delete(\"/\");\n\n return files;\n };\n\n let changedFiles = await getChangedFiles();\n const hasLocalChanges = changedFiles.size > 0;\n if (hasLocalChanges) {\n this.log.info(\"local files have changed\", {\n remoteFilesVersion,\n hasRemoteChanges,\n hasLocalChanges,\n changed: Array.from(changedFiles.keys()),\n });\n\n println(\"Local files have changed since you last synced\");\n printPaths(\"-\", Array.from(changedFiles.keys()), [], { limit: changedFiles.size });\n println();\n }\n\n let action: Action | undefined;\n if (hasLocalChanges) {\n action = await select({\n message: hasRemoteChanges ? \"Remote files have also changed. How would you like to proceed?\" : \"How would you like to proceed?\",\n choices: [Action.CANCEL, Action.MERGE, Action.RESET],\n });\n }\n\n // get all the changed files again in case more changed\n changedFiles = await getChangedFiles();\n\n switch (action) {\n case Action.MERGE: {\n this.log.info(\"merging local changes\", {\n remoteFilesVersion,\n hasRemoteChanges,\n hasLocalChanges,\n changed: Array.from(changedFiles.keys()),\n });\n\n // We purposefully don't write the returned files version here\n // because we haven't received its associated files yet. This\n // will cause us to receive the remote files that have changed\n // since the last sync (+ the local files that we just\n // published)\n await this.graphql.query({\n query: PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n variables: {\n input: {\n expectedRemoteFilesVersion: remoteFilesVersion,\n changed: await pMap(changedFiles, async ([normalizedPath, stats]) => ({\n path: normalizedPath,\n mode: stats.mode,\n content: stats.isDirectory() ? \"\" : await fs.readFile(this.filesync.absolute(normalizedPath), \"base64\"),\n encoding: FileSyncEncoding.Base64,\n })),\n deleted: [],\n },\n },\n });\n break;\n }\n case Action.RESET: {\n this.log.info(\"resetting local changes\", {\n remoteFilesVersion,\n hasRemoteChanges,\n hasLocalChanges,\n changed: Array.from(changedFiles.keys()),\n });\n\n // delete all the local files that have changed since the last\n // sync and set the files version to 0 so we receive all the\n // remote files again, including any files that we just deleted\n // that still exist\n await this.filesync.write(0n, [], changedFiles.keys(), true);\n break;\n }\n case Action.CANCEL: {\n process.exit(0);\n }\n }\n }\n\n /**\n * Runs the sync process until it is stopped or an error occurs.\n */\n async run(): Promise<void> {\n let error: unknown;\n const stopped = new PromiseSignal();\n\n const recentRemoteChangesInterval = setInterval(() => {\n for (const [path, timestamp] of this.recentRemoteChanges) {\n if (dayjs().isAfter(timestamp + ms(\"5s\"))) {\n // this change should have been seen by now, so remove it\n this.recentRemoteChanges.delete(path);\n }\n }\n }, ms(\"1s\")).unref();\n\n this.stop = async (e?: unknown) => {\n if (this.status !== SyncStatus.RUNNING) {\n return;\n }\n\n this.status = SyncStatus.STOPPING;\n error = e;\n\n this.log.info(\"stopping\", { error });\n\n try {\n clearInterval(recentRemoteChangesInterval);\n unsubscribe();\n this.watcher.removeAllListeners();\n this.publish.flush();\n await this.queue.onIdle();\n } finally {\n await Promise.allSettled([this.watcher.close(), this.graphql.dispose()]);\n\n this.status = SyncStatus.STOPPED;\n stopped.resolve();\n this.log.info(\"stopped\");\n }\n };\n\n for (const signal of [\"SIGINT\", \"SIGTERM\"] as const) {\n process.on(signal, () => {\n if (this.status !== SyncStatus.RUNNING) {\n return;\n }\n\n println` Stopping... {gray (press Ctrl+C again to force)}`;\n void this.stop();\n\n // When ggt is run via npx, and the user presses Ctrl+C, npx sends SIGINT twice in quick succession. In order to prevent the second\n // SIGINT from triggering the force exit listener, we wait a bit before registering it. This is a bit of a hack, but it works.\n setTimeout(() => {\n process.once(signal, () => {\n println(\" Exiting immediately. Note that files may not have finished syncing.\");\n process.exit(1);\n });\n }, 100).unref();\n });\n }\n\n const unsubscribe = this.graphql.subscribe(\n {\n query: REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION,\n variables: () => ({ localFilesVersion: String(this.filesync.filesVersion) }),\n },\n {\n error: (error) => void this.stop(error),\n next: ({ remoteFileSyncEvents }) => {\n const remoteFilesVersion = remoteFileSyncEvents.remoteFilesVersion;\n\n // we always ignore .gadget/ files so that we don't publish them (they're managed by gadget), but we still want to receive them\n const filterIgnored = (event: { path: string }) => event.path.startsWith(\".gadget/\") || !this.filesync.ignores(event.path);\n const changed = remoteFileSyncEvents.changed.filter(filterIgnored);\n const deleted = remoteFileSyncEvents.deleted.filter(filterIgnored);\n\n this.log.info(\"received files\", {\n remoteFilesVersion,\n changed: changed.map((x) => x.path),\n deleted: deleted.map((x) => x.path),\n });\n\n this._enqueue(async () => {\n // add all the non-ignored files and directories we're about\n // to touch to recentRemoteChanges so that we don't send\n // them back\n for (const file of [...changed, ...deleted].filter((file) => !this.filesync.ignores(file.path))) {\n this.recentRemoteChanges.set(file.path, Date.now());\n\n let dir = path.dirname(file.path);\n while (dir !== \".\") {\n this.recentRemoteChanges.set(dir + \"/\", Date.now());\n dir = path.dirname(dir);\n }\n }\n\n if (changed.length > 0 || deleted.length > 0) {\n println`Received {gray ${dayjs().format(\"hh:mm:ss A\")}}`;\n printPaths(\n \"←\",\n changed.map((x) => x.path),\n deleted.map((x) => x.path),\n );\n }\n\n await this.filesync.write(\n remoteFilesVersion,\n changed,\n deleted.map((x) => x.path),\n );\n\n if (changed.some((x) => x.path === \"yarn.lock\")) {\n await execa(\"yarn\", [\"install\"], { cwd: this.filesync.dir }).catch(noop);\n }\n });\n },\n },\n );\n\n const localFilesBuffer = new Map<\n string,\n | { mode: number; isDirectory: boolean }\n | { isDeleted: true; isDirectory: boolean }\n | { mode: number; oldPath: string; newPath: string; isDirectory: boolean }\n >();\n\n this.publish = debounce(this.args[\"--file-push-delay\"], () => {\n const localFiles = new Map(localFilesBuffer.entries());\n localFilesBuffer.clear();\n\n this._enqueue(async () => {\n const changed: FileSyncChangedEventInput[] = [];\n const deleted: FileSyncDeletedEventInput[] = [];\n\n await pMap(localFiles, async ([normalizedPath, file]) => {\n if (\"isDeleted\" in file) {\n deleted.push({ path: normalizedPath });\n return;\n }\n\n try {\n changed.push({\n path: normalizedPath,\n oldPath: \"oldPath\" in file ? file.oldPath : undefined,\n mode: file.mode,\n content: file.isDirectory ? \"\" : await fs.readFile(this.filesync.absolute(normalizedPath), FileSyncEncoding.Base64),\n encoding: FileSyncEncoding.Base64,\n });\n } catch (error) {\n // A file could have been changed and then deleted before we process the change event, so the readFile\n // above will raise an ENOENT. This is normal operation, so just ignore this event.\n swallowEnoent(error);\n }\n });\n\n if (changed.length === 0 && deleted.length === 0) {\n return;\n }\n\n const { publishFileSyncEvents } = await this.graphql.query({\n query: PUBLISH_FILE_SYNC_EVENTS_MUTATION,\n variables: { input: { expectedRemoteFilesVersion: String(this.filesync.filesVersion), changed, deleted } },\n });\n\n await this.filesync.write(publishFileSyncEvents.remoteFilesVersion, [], []);\n\n println`Sent {gray ${dayjs().format(\"hh:mm:ss A\")}}`;\n printPaths(\n \"→\",\n changed.map((x) => x.path),\n deleted.map((x) => x.path),\n );\n });\n });\n\n this.watcher = new FSWatcher(this.filesync.dir, {\n // don't emit an event for every watched file on boot\n ignoreInitial: true,\n ignore: (path: string) => this.filesync.ignores(path),\n renameDetection: true,\n recursive: true,\n debounce: this.args[\"--file-watch-debounce\"],\n pollingInterval: this.args[\"--file-watch-poll-interval\"],\n pollingTimeout: this.args[\"--file-watch-poll-timeout\"],\n renameTimeout: this.args[\"--file-watch-rename-timeout\"],\n });\n\n this.watcher.once(\"error\", (error) => void this.stop(error));\n\n this.watcher.on(\"all\", (event: string, absolutePath: string, renamedPath: string) => {\n const filepath = event === \"rename\" || event === \"renameDir\" ? renamedPath : absolutePath;\n const isDirectory = event === \"renameDir\" || event === \"addDir\" || event === \"unlinkDir\";\n const normalizedPath = this.filesync.normalize(filepath, isDirectory);\n\n this.log.debug(\"file event\", {\n event,\n path: normalizedPath,\n isDirectory,\n recentRemoteChanges: Array.from(this.recentRemoteChanges.keys()),\n });\n\n if (filepath === this.filesync.absolute(\".ignore\")) {\n this.filesync.reloadIgnorePaths();\n } else if (this.filesync.ignores(filepath)) {\n return;\n }\n\n if (this.recentRemoteChanges.delete(normalizedPath)) {\n return;\n }\n\n switch (event) {\n case \"add\":\n case \"addDir\":\n case \"change\": {\n const stats = fs.statSync(filepath);\n localFilesBuffer.set(normalizedPath, { mode: stats.mode, isDirectory });\n break;\n }\n case \"unlink\":\n case \"unlinkDir\": {\n localFilesBuffer.set(normalizedPath, { isDeleted: true, isDirectory });\n break;\n }\n case \"rename\":\n case \"renameDir\": {\n const stats = fs.statSync(filepath);\n localFilesBuffer.set(normalizedPath, {\n oldPath: this.filesync.normalize(absolutePath, isDirectory),\n newPath: normalizedPath,\n isDirectory,\n mode: stats.mode,\n });\n break;\n }\n }\n\n this.publish();\n });\n\n this.status = SyncStatus.RUNNING;\n\n println();\n println`\n {bold ggt v${config.version}}\n\n App ${this.filesync.app.slug}\n Editor https://${this.filesync.app.slug}.gadget.app/edit\n Playground https://${this.filesync.app.slug}.gadget.app/api/graphql/playground\n Docs https://docs.gadget.dev/api/${this.filesync.app.slug}\n\n {underline Endpoints} ${\n this.filesync.app.hasSplitEnvironments\n ? `\n • https://${this.filesync.app.primaryDomain}\n • https://${this.filesync.app.slug}--development.gadget.app`\n : `\n • https://${this.filesync.app.primaryDomain}`\n }\n\n Watching for file changes... {gray Press Ctrl+C to stop}\n `;\n println();\n\n await stopped;\n\n if (error) {\n notify({ subtitle: \"Uh oh!\", message: \"An error occurred while syncing files\" });\n throw error as Error;\n } else {\n println(\"Goodbye!\");\n }\n }\n\n /**\n * Enqueues a function that handles file-sync events onto the {@linkcode queue}.\n *\n * @param fn The function to enqueue.\n */\n private _enqueue(fn: () => Promise<unknown>): void {\n void this.queue.add(fn).catch(this.stop);\n }\n}\n\nconst sync = new Sync();\nexport const init = sync.init.bind(sync);\nexport const run = sync.run.bind(sync);\n"],"names":["arg","dayjs","execa","fs","ms","path","pMap","PQueue","FSWatcher","which","FileSyncEncoding","AppArg","config","debounce","defaults","EditGraphQL","YarnNotFoundError","FileSync","PUBLISH_FILE_SYNC_EVENTS_MUTATION","REMOTE_FILES_VERSION_QUERY","REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION","printPaths","swallowEnoent","createLogger","noop","notify","println","sprint","PromiseSignal","select","getUserOrLogin","usage","SyncStatus","Action","argSpec","Boolean","Number","Sync","init","rootArgs","args","argv","_","sync","nothrow","user","filesync","dir","app","force","extraIgnorePaths","graphql","remoteFilesVersion","query","hasRemoteChanges","BigInt","filesVersion","getChangedFiles","files","Map","absolutePath","stats","walkDir","mtime","getTime","set","normalize","isDirectory","delete","changedFiles","hasLocalChanges","size","log","info","changed","Array","from","keys","limit","action","message","choices","variables","input","expectedRemoteFilesVersion","normalizedPath","mode","content","readFile","absolute","encoding","Base64","deleted","write","process","exit","run","error","stopped","recentRemoteChangesInterval","setInterval","timestamp","recentRemoteChanges","isAfter","unref","stop","e","status","clearInterval","unsubscribe","watcher","removeAllListeners","publish","flush","queue","onIdle","Promise","allSettled","close","dispose","resolve","signal","on","setTimeout","once","subscribe","localFilesVersion","String","next","remoteFileSyncEvents","filterIgnored","event","startsWith","ignores","filter","map","x","_enqueue","file","Date","now","dirname","length","format","some","cwd","catch","localFilesBuffer","localFiles","entries","clear","push","oldPath","undefined","publishFileSyncEvents","ignoreInitial","ignore","renameDetection","recursive","pollingInterval","pollingTimeout","renameTimeout","renamedPath","filepath","debug","reloadIgnorePaths","statSync","isDeleted","newPath","version","slug","hasSplitEnvironments","primaryDomain","subtitle","fn","add","concurrency","bind"],"mappings":";AAAA,OAAOA,SAAS,MAAM;AACtB,OAAOC,WAAW,QAAQ;AAC1B,SAASC,KAAK,QAAQ,QAAQ;AAE9B,OAAOC,QAAQ,WAAW;AAC1B,OAAOC,QAAQ,KAAK;AACpB,OAAOC,UAAU,YAAY;AAC7B,OAAOC,UAAU,QAAQ;AACzB,OAAOC,YAAY,UAAU;AAE7B,OAAOC,eAAe,UAAU;AAChC,OAAOC,WAAW,QAAQ;AAC1B,SAASC,gBAAgB,QAAwE,8BAA8B;AAC/H,SAASC,MAAM,QAAQ,sBAAsB;AAC7C,SAASC,MAAM,QAAQ,wBAAwB;AAC/C,SAASC,QAAQ,QAA4B,0BAA0B;AACvE,SAASC,QAAQ,QAAQ,0BAA0B;AACnD,SAASC,WAAW,QAAQ,8BAA8B;AAC1D,SAASC,iBAAiB,QAAQ,wBAAwB;AAC1D,SACEC,QAAQ,EACRC,iCAAiC,EACjCC,0BAA0B,EAC1BC,oCAAoC,EACpCC,UAAU,QACL,0BAA0B;AACjC,SAASC,aAAa,QAAQ,oBAAoB;AAClD,SAASC,YAAY,QAAQ,qBAAqB;AAClD,SAASC,IAAI,QAAQ,sBAAsB;AAC3C,SAASC,MAAM,QAAQ,wBAAwB;AAC/C,SAASC,OAAO,EAAEC,MAAM,QAAQ,wBAAwB;AACxD,SAASC,aAAa,QAAQ,yBAAyB;AACvD,SAASC,MAAM,QAAQ,wBAAwB;AAC/C,SAASC,cAAc,QAAQ,sBAAsB;AAGrD,OAAO,MAAMC,QAAQJ,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6E5B,CAAC,CAAC;;UAEUK;;;;;GAAAA,eAAAA;;UAOAC;;;;GAAAA,WAAAA;AAMZ,MAAMC,UAAU;IACd,MAAM;IACN,SAASvB;IACT,WAAWwB;IACX,qBAAqBC;IACrB,yBAAyBA;IACzB,8BAA8BA;IAC9B,6BAA6BA;IAC7B,+BAA+BA;AACjC;AAEA,OAAO,MAAMC;IAwDX;;;;;;;GAOC,GACD,MAAMC,KAAKC,QAAkB,EAAiB;QAC5C,IAAI,CAACC,IAAI,GAAG1B,SAASd,IAAIkC,SAAS;YAAEO,MAAMF,SAASG,CAAC;QAAC,IAAI;YACvD,qBAAqB;YACrB,yBAAyB;YACzB,8BAA8B;YAC9B,6BAA6B;YAC7B,+BAA+B;QACjC;QAEA,IAAI,CAACjC,MAAMkC,IAAI,CAAC,QAAQ;YAAEC,SAAS;QAAK,IAAI;YAC1C,MAAM,IAAI5B;QACZ;QAEA,MAAM6B,OAAO,MAAMf;QAEnB,IAAI,CAACgB,QAAQ,GAAG,MAAM7B,SAASqB,IAAI,CAACO,MAAM;YACxCE,KAAK,IAAI,CAACP,IAAI,CAACE,CAAC,CAAC,EAAE;YACnBM,KAAK,IAAI,CAACR,IAAI,CAAC,QAAQ;YACvBS,OAAO,IAAI,CAACT,IAAI,CAAC,UAAU;YAC3BU,kBAAkB;gBAAC;aAAU;QAC/B;QAEA,IAAI,CAACC,OAAO,GAAG,IAAIpC,YAAY,IAAI,CAAC+B,QAAQ,CAACE,GAAG;QAEhD,MAAM,EAAEI,kBAAkB,EAAE,GAAG,MAAM,IAAI,CAACD,OAAO,CAACE,KAAK,CAAC;YAAEA,OAAOlC;QAA2B;QAC5F,MAAMmC,mBAAmBC,OAAOH,sBAAsB,IAAI,CAACN,QAAQ,CAACU,YAAY;QAEhF,MAAMC,kBAAkB;YACtB,MAAMC,QAAQ,IAAIC;YAClB,WAAW,MAAM,CAACC,cAAcC,MAAM,IAAI,IAAI,CAACf,QAAQ,CAACgB,OAAO,GAAI;gBACjE,IAAID,MAAME,KAAK,CAACC,OAAO,KAAK,IAAI,CAAClB,QAAQ,CAACiB,KAAK,EAAE;oBAC/CL,MAAMO,GAAG,CAAC,IAAI,CAACnB,QAAQ,CAACoB,SAAS,CAACN,cAAcC,MAAMM,WAAW,KAAKN;gBACxE;YACF;YAEA,mCAAmC;YACnCH,MAAMU,MAAM,CAAC;YAEb,OAAOV;QACT;QAEA,IAAIW,eAAe,MAAMZ;QACzB,MAAMa,kBAAkBD,aAAaE,IAAI,GAAG;QAC5C,IAAID,iBAAiB;YACnB,IAAI,CAACE,GAAG,CAACC,IAAI,CAAC,4BAA4B;gBACxCrB;gBACAE;gBACAgB;gBACAI,SAASC,MAAMC,IAAI,CAACP,aAAaQ,IAAI;YACvC;YAEAnD,QAAQ;YACRL,WAAW,KAAKsD,MAAMC,IAAI,CAACP,aAAaQ,IAAI,KAAK,EAAE,EAAE;gBAAEC,OAAOT,aAAaE,IAAI;YAAC;YAChF7C;QACF;QAEA,IAAIqD;QACJ,IAAIT,iBAAiB;YACnBS,SAAS,MAAMlD,OAAO;gBACpBmD,SAAS1B,mBAAmB,mEAAmE;gBAC/F2B,SAAS;;;;iBAA2C;YACtD;QACF;QAEA,uDAAuD;QACvDZ,eAAe,MAAMZ;QAErB,OAAQsB;YACN;gBAAmB;oBACjB,IAAI,CAACP,GAAG,CAACC,IAAI,CAAC,yBAAyB;wBACrCrB;wBACAE;wBACAgB;wBACAI,SAASC,MAAMC,IAAI,CAACP,aAAaQ,IAAI;oBACvC;oBAEA,8DAA8D;oBAC9D,6DAA6D;oBAC7D,8DAA8D;oBAC9D,sDAAsD;oBACtD,aAAa;oBACb,MAAM,IAAI,CAAC1B,OAAO,CAACE,KAAK,CAAC;wBACvBA,OAAOnC;wBACPgE,WAAW;4BACTC,OAAO;gCACLC,4BAA4BhC;gCAC5BsB,SAAS,MAAMpE,KAAK+D,cAAc,OAAO,CAACgB,gBAAgBxB,MAAM,GAAM,CAAA;wCACpExD,MAAMgF;wCACNC,MAAMzB,MAAMyB,IAAI;wCAChBC,SAAS1B,MAAMM,WAAW,KAAK,KAAK,MAAMhE,GAAGqF,QAAQ,CAAC,IAAI,CAAC1C,QAAQ,CAAC2C,QAAQ,CAACJ,iBAAiB;wCAC9FK,UAAUhF,iBAAiBiF,MAAM;oCACnC,CAAA;gCACAC,SAAS,EAAE;4BACb;wBACF;oBACF;oBACA;gBACF;YACA;gBAAmB;oBACjB,IAAI,CAACpB,GAAG,CAACC,IAAI,CAAC,2BAA2B;wBACvCrB;wBACAE;wBACAgB;wBACAI,SAASC,MAAMC,IAAI,CAACP,aAAaQ,IAAI;oBACvC;oBAEA,8DAA8D;oBAC9D,4DAA4D;oBAC5D,+DAA+D;oBAC/D,mBAAmB;oBACnB,MAAM,IAAI,CAAC/B,QAAQ,CAAC+C,KAAK,CAAC,EAAE,EAAE,EAAE,EAAExB,aAAaQ,IAAI,IAAI;oBACvD;gBACF;YACA;gBAAoB;oBAClBiB,QAAQC,IAAI,CAAC;gBACf;QACF;IACF;IAEA;;GAEC,GACD,MAAMC,MAAqB;QACzB,IAAIC;QACJ,MAAMC,UAAU,IAAItE;QAEpB,MAAMuE,8BAA8BC,YAAY;YAC9C,KAAK,MAAM,CAAC/F,MAAMgG,UAAU,IAAI,IAAI,CAACC,mBAAmB,CAAE;gBACxD,IAAIrG,QAAQsG,OAAO,CAACF,YAAYjG,GAAG,QAAQ;oBACzC,yDAAyD;oBACzD,IAAI,CAACkG,mBAAmB,CAAClC,MAAM,CAAC/D;gBAClC;YACF;QACF,GAAGD,GAAG,OAAOoG,KAAK;QAElB,IAAI,CAACC,IAAI,GAAG,OAAOC;YACjB,IAAI,IAAI,CAACC,MAAM,QAAyB;gBACtC;YACF;YAEA,IAAI,CAACA,MAAM;YACXV,QAAQS;YAER,IAAI,CAAClC,GAAG,CAACC,IAAI,CAAC,YAAY;gBAAEwB;YAAM;YAElC,IAAI;gBACFW,cAAcT;gBACdU;gBACA,IAAI,CAACC,OAAO,CAACC,kBAAkB;gBAC/B,IAAI,CAACC,OAAO,CAACC,KAAK;gBAClB,MAAM,IAAI,CAACC,KAAK,CAACC,MAAM;YACzB,SAAU;gBACR,MAAMC,QAAQC,UAAU,CAAC;oBAAC,IAAI,CAACP,OAAO,CAACQ,KAAK;oBAAI,IAAI,CAACnE,OAAO,CAACoE,OAAO;iBAAG;gBAEvE,IAAI,CAACZ,MAAM;gBACXT,QAAQsB,OAAO;gBACf,IAAI,CAAChD,GAAG,CAACC,IAAI,CAAC;YAChB;QACF;QAEA,KAAK,MAAMgD,UAAU;YAAC;YAAU;SAAU,CAAW;YACnD3B,QAAQ4B,EAAE,CAACD,QAAQ;gBACjB,IAAI,IAAI,CAACd,MAAM,QAAyB;oBACtC;gBACF;gBAEAjF,OAAO,CAAC,iDAAiD,CAAC;gBAC1D,KAAK,IAAI,CAAC+E,IAAI;gBAEd,mIAAmI;gBACnI,8HAA8H;gBAC9HkB,WAAW;oBACT7B,QAAQ8B,IAAI,CAACH,QAAQ;wBACnB/F,QAAQ;wBACRoE,QAAQC,IAAI,CAAC;oBACf;gBACF,GAAG,KAAKS,KAAK;YACf;QACF;QAEA,MAAMK,cAAc,IAAI,CAAC1D,OAAO,CAAC0E,SAAS,CACxC;YACExE,OAAOjC;YACP8D,WAAW,IAAO,CAAA;oBAAE4C,mBAAmBC,OAAO,IAAI,CAACjF,QAAQ,CAACU,YAAY;gBAAE,CAAA;QAC5E,GACA;YACEyC,OAAO,CAACA,QAAU,KAAK,IAAI,CAACQ,IAAI,CAACR;YACjC+B,MAAM,CAAC,EAAEC,oBAAoB,EAAE;gBAC7B,MAAM7E,qBAAqB6E,qBAAqB7E,kBAAkB;gBAElE,+HAA+H;gBAC/H,MAAM8E,gBAAgB,CAACC,QAA4BA,MAAM9H,IAAI,CAAC+H,UAAU,CAAC,eAAe,CAAC,IAAI,CAACtF,QAAQ,CAACuF,OAAO,CAACF,MAAM9H,IAAI;gBACzH,MAAMqE,UAAUuD,qBAAqBvD,OAAO,CAAC4D,MAAM,CAACJ;gBACpD,MAAMtC,UAAUqC,qBAAqBrC,OAAO,CAAC0C,MAAM,CAACJ;gBAEpD,IAAI,CAAC1D,GAAG,CAACC,IAAI,CAAC,kBAAkB;oBAC9BrB;oBACAsB,SAASA,QAAQ6D,GAAG,CAAC,CAACC,IAAMA,EAAEnI,IAAI;oBAClCuF,SAASA,QAAQ2C,GAAG,CAAC,CAACC,IAAMA,EAAEnI,IAAI;gBACpC;gBAEA,IAAI,CAACoI,QAAQ,CAAC;oBACZ,4DAA4D;oBAC5D,wDAAwD;oBACxD,YAAY;oBACZ,KAAK,MAAMC,QAAQ;2BAAIhE;2BAAYkB;qBAAQ,CAAC0C,MAAM,CAAC,CAACI,OAAS,CAAC,IAAI,CAAC5F,QAAQ,CAACuF,OAAO,CAACK,KAAKrI,IAAI,GAAI;wBAC/F,IAAI,CAACiG,mBAAmB,CAACrC,GAAG,CAACyE,KAAKrI,IAAI,EAAEsI,KAAKC,GAAG;wBAEhD,IAAI7F,MAAM1C,KAAKwI,OAAO,CAACH,KAAKrI,IAAI;wBAChC,MAAO0C,QAAQ,IAAK;4BAClB,IAAI,CAACuD,mBAAmB,CAACrC,GAAG,CAAClB,MAAM,KAAK4F,KAAKC,GAAG;4BAChD7F,MAAM1C,KAAKwI,OAAO,CAAC9F;wBACrB;oBACF;oBAEA,IAAI2B,QAAQoE,MAAM,GAAG,KAAKlD,QAAQkD,MAAM,GAAG,GAAG;wBAC5CpH,OAAO,CAAC,eAAe,EAAEzB,QAAQ8I,MAAM,CAAC,cAAc,CAAC,CAAC;wBACxD1H,WACE,KACAqD,QAAQ6D,GAAG,CAAC,CAACC,IAAMA,EAAEnI,IAAI,GACzBuF,QAAQ2C,GAAG,CAAC,CAACC,IAAMA,EAAEnI,IAAI;oBAE7B;oBAEA,MAAM,IAAI,CAACyC,QAAQ,CAAC+C,KAAK,CACvBzC,oBACAsB,SACAkB,QAAQ2C,GAAG,CAAC,CAACC,IAAMA,EAAEnI,IAAI;oBAG3B,IAAIqE,QAAQsE,IAAI,CAAC,CAACR,IAAMA,EAAEnI,IAAI,KAAK,cAAc;wBAC/C,MAAMH,MAAM,QAAQ;4BAAC;yBAAU,EAAE;4BAAE+I,KAAK,IAAI,CAACnG,QAAQ,CAACC,GAAG;wBAAC,GAAGmG,KAAK,CAAC1H;oBACrE;gBACF;YACF;QACF;QAGF,MAAM2H,mBAAmB,IAAIxF;QAO7B,IAAI,CAACqD,OAAO,GAAGnG,SAAS,IAAI,CAAC2B,IAAI,CAAC,oBAAoB,EAAE;YACtD,MAAM4G,aAAa,IAAIzF,IAAIwF,iBAAiBE,OAAO;YACnDF,iBAAiBG,KAAK;YAEtB,IAAI,CAACb,QAAQ,CAAC;gBACZ,MAAM/D,UAAuC,EAAE;gBAC/C,MAAMkB,UAAuC,EAAE;gBAE/C,MAAMtF,KAAK8I,YAAY,OAAO,CAAC/D,gBAAgBqD,KAAK;oBAClD,IAAI,eAAeA,MAAM;wBACvB9C,QAAQ2D,IAAI,CAAC;4BAAElJ,MAAMgF;wBAAe;wBACpC;oBACF;oBAEA,IAAI;wBACFX,QAAQ6E,IAAI,CAAC;4BACXlJ,MAAMgF;4BACNmE,SAAS,aAAad,OAAOA,KAAKc,OAAO,GAAGC;4BAC5CnE,MAAMoD,KAAKpD,IAAI;4BACfC,SAASmD,KAAKvE,WAAW,GAAG,KAAK,MAAMhE,GAAGqF,QAAQ,CAAC,IAAI,CAAC1C,QAAQ,CAAC2C,QAAQ,CAACJ,iBAAiB3E,iBAAiBiF,MAAM;4BAClHD,UAAUhF,iBAAiBiF,MAAM;wBACnC;oBACF,EAAE,OAAOM,OAAO;wBACd,sGAAsG;wBACtG,mFAAmF;wBACnF3E,cAAc2E;oBAChB;gBACF;gBAEA,IAAIvB,QAAQoE,MAAM,KAAK,KAAKlD,QAAQkD,MAAM,KAAK,GAAG;oBAChD;gBACF;gBAEA,MAAM,EAAEY,qBAAqB,EAAE,GAAG,MAAM,IAAI,CAACvG,OAAO,CAACE,KAAK,CAAC;oBACzDA,OAAOnC;oBACPgE,WAAW;wBAAEC,OAAO;4BAAEC,4BAA4B2C,OAAO,IAAI,CAACjF,QAAQ,CAACU,YAAY;4BAAGkB;4BAASkB;wBAAQ;oBAAE;gBAC3G;gBAEA,MAAM,IAAI,CAAC9C,QAAQ,CAAC+C,KAAK,CAAC6D,sBAAsBtG,kBAAkB,EAAE,EAAE,EAAE,EAAE;gBAE1E1B,OAAO,CAAC,WAAW,EAAEzB,QAAQ8I,MAAM,CAAC,cAAc,CAAC,CAAC;gBACpD1H,WACE,KACAqD,QAAQ6D,GAAG,CAAC,CAACC,IAAMA,EAAEnI,IAAI,GACzBuF,QAAQ2C,GAAG,CAAC,CAACC,IAAMA,EAAEnI,IAAI;YAE7B;QACF;QAEA,IAAI,CAACyG,OAAO,GAAG,IAAItG,UAAU,IAAI,CAACsC,QAAQ,CAACC,GAAG,EAAE;YAC9C,qDAAqD;YACrD4G,eAAe;YACfC,QAAQ,CAACvJ,OAAiB,IAAI,CAACyC,QAAQ,CAACuF,OAAO,CAAChI;YAChDwJ,iBAAiB;YACjBC,WAAW;YACXjJ,UAAU,IAAI,CAAC2B,IAAI,CAAC,wBAAwB;YAC5CuH,iBAAiB,IAAI,CAACvH,IAAI,CAAC,6BAA6B;YACxDwH,gBAAgB,IAAI,CAACxH,IAAI,CAAC,4BAA4B;YACtDyH,eAAe,IAAI,CAACzH,IAAI,CAAC,8BAA8B;QACzD;QAEA,IAAI,CAACsE,OAAO,CAACc,IAAI,CAAC,SAAS,CAAC3B,QAAU,KAAK,IAAI,CAACQ,IAAI,CAACR;QAErD,IAAI,CAACa,OAAO,CAACY,EAAE,CAAC,OAAO,CAACS,OAAevE,cAAsBsG;YAC3D,MAAMC,WAAWhC,UAAU,YAAYA,UAAU,cAAc+B,cAActG;YAC7E,MAAMO,cAAcgE,UAAU,eAAeA,UAAU,YAAYA,UAAU;YAC7E,MAAM9C,iBAAiB,IAAI,CAACvC,QAAQ,CAACoB,SAAS,CAACiG,UAAUhG;YAEzD,IAAI,CAACK,GAAG,CAAC4F,KAAK,CAAC,cAAc;gBAC3BjC;gBACA9H,MAAMgF;gBACNlB;gBACAmC,qBAAqB3B,MAAMC,IAAI,CAAC,IAAI,CAAC0B,mBAAmB,CAACzB,IAAI;YAC/D;YAEA,IAAIsF,aAAa,IAAI,CAACrH,QAAQ,CAAC2C,QAAQ,CAAC,YAAY;gBAClD,IAAI,CAAC3C,QAAQ,CAACuH,iBAAiB;YACjC,OAAO,IAAI,IAAI,CAACvH,QAAQ,CAACuF,OAAO,CAAC8B,WAAW;gBAC1C;YACF;YAEA,IAAI,IAAI,CAAC7D,mBAAmB,CAAClC,MAAM,CAACiB,iBAAiB;gBACnD;YACF;YAEA,OAAQ8C;gBACN,KAAK;gBACL,KAAK;gBACL,KAAK;oBAAU;wBACb,MAAMtE,QAAQ1D,GAAGmK,QAAQ,CAACH;wBAC1BhB,iBAAiBlF,GAAG,CAACoB,gBAAgB;4BAAEC,MAAMzB,MAAMyB,IAAI;4BAAEnB;wBAAY;wBACrE;oBACF;gBACA,KAAK;gBACL,KAAK;oBAAa;wBAChBgF,iBAAiBlF,GAAG,CAACoB,gBAAgB;4BAAEkF,WAAW;4BAAMpG;wBAAY;wBACpE;oBACF;gBACA,KAAK;gBACL,KAAK;oBAAa;wBAChB,MAAMN,QAAQ1D,GAAGmK,QAAQ,CAACH;wBAC1BhB,iBAAiBlF,GAAG,CAACoB,gBAAgB;4BACnCmE,SAAS,IAAI,CAAC1G,QAAQ,CAACoB,SAAS,CAACN,cAAcO;4BAC/CqG,SAASnF;4BACTlB;4BACAmB,MAAMzB,MAAMyB,IAAI;wBAClB;wBACA;oBACF;YACF;YAEA,IAAI,CAAC0B,OAAO;QACd;QAEA,IAAI,CAACL,MAAM;QAEXjF;QACAA,OAAO,CAAC;iBACK,EAAEd,OAAO6J,OAAO,CAAC;;kBAEhB,EAAE,IAAI,CAAC3H,QAAQ,CAACE,GAAG,CAAC0H,IAAI,CAAC;0BACjB,EAAE,IAAI,CAAC5H,QAAQ,CAACE,GAAG,CAAC0H,IAAI,CAAC;0BACzB,EAAE,IAAI,CAAC5H,QAAQ,CAACE,GAAG,CAAC0H,IAAI,CAAC;8CACL,EAAE,IAAI,CAAC5H,QAAQ,CAACE,GAAG,CAAC0H,IAAI,CAAC;;4BAE3C,EACpB,IAAI,CAAC5H,QAAQ,CAACE,GAAG,CAAC2H,oBAAoB,GAClC,CAAC;kBACK,EAAE,IAAI,CAAC7H,QAAQ,CAACE,GAAG,CAAC4H,aAAa,CAAC;kBAClC,EAAE,IAAI,CAAC9H,QAAQ,CAACE,GAAG,CAAC0H,IAAI,CAAC,wBAAwB,CAAC,GACxD,CAAC;kBACK,EAAE,IAAI,CAAC5H,QAAQ,CAACE,GAAG,CAAC4H,aAAa,CAAC,CAAC,CAC9C;;;IAGH,CAAC;QACDlJ;QAEA,MAAMwE;QAEN,IAAID,OAAO;YACTxE,OAAO;gBAAEoJ,UAAU;gBAAU7F,SAAS;YAAwC;YAC9E,MAAMiB;QACR,OAAO;YACLvE,QAAQ;QACV;IACF;IAEA;;;;GAIC,GACD,AAAQ+G,SAASqC,EAA0B,EAAQ;QACjD,KAAK,IAAI,CAAC5D,KAAK,CAAC6D,GAAG,CAACD,IAAI5B,KAAK,CAAC,IAAI,CAACzC,IAAI;IACzC;;QA/cAjE,uBAAAA,QAAAA,KAAAA;QAEA;;GAEC,GACDmE,uBAAAA;QAEA;;;;GAIC,GACDL,uBAAAA,uBAAsB,IAAI3C;QAE1B;;GAEC,GACDuD,uBAAAA,SAAQ,IAAI3G,OAAO;YAAEyK,aAAa;QAAE;QAEpC;;GAEC,GACD7H,uBAAAA,WAAAA,KAAAA;QAEA;;GAEC,GACD2D,uBAAAA,WAAAA,KAAAA;QAEA;;GAEC,GACDhE,uBAAAA,YAAAA,KAAAA;QAEA;;GAEC,GACDkE,uBAAAA,WAAAA,KAAAA;QAEA;;GAEC,GACDP,uBAAAA,QAAAA,KAAAA;QAEA;;GAEC,GACDjC,uBAAAA,OAAMjD,aAAa,QAAQ;YACzB,OAAO;gBACLyB,KAAK,IAAI,CAACF,QAAQ,CAACE,GAAG,CAAC0H,IAAI;gBAC3BlH,cAAcuE,OAAO,IAAI,CAACjF,QAAQ,CAACU,YAAY;gBAC/CO,OAAO,IAAI,CAACjB,QAAQ,CAACiB,KAAK;YAC5B;QACF;;AA2ZF;AAEA,MAAMpB,OAAO,IAAIN;AACjB,OAAO,MAAMC,OAAOK,KAAKL,IAAI,CAAC2I,IAAI,CAACtI,MAAM;AACzC,OAAO,MAAMqD,MAAMrD,KAAKqD,GAAG,CAACiF,IAAI,CAACtI,MAAM"}
@@ -1,4 +1,3 @@
1
- import _ from "lodash";
2
1
  import { z } from "zod";
3
2
  import { config } from "./config.js";
4
3
  import { http, loadCookie } from "./http.js";
@@ -27,7 +26,7 @@ export const App = z.object({
27
26
  responseType: "json",
28
27
  resolveBodyOnly: true
29
28
  });
30
- return _.map(z.array(App).parse(json), (app)=>({
29
+ return z.array(App).parse(json).map((app)=>({
31
30
  ...app,
32
31
  user
33
32
  }));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/app.ts"],"sourcesContent":["import _ from \"lodash\";\nimport { z } from \"zod\";\nimport { config } from \"./config.js\";\nimport { http, loadCookie } from \"./http.js\";\nimport type { User } from \"./user.js\";\n\nexport const App = z.object({\n id: z.union([z.string(), z.number(), z.bigint()]),\n slug: z.string(),\n primaryDomain: z.string(),\n hasSplitEnvironments: z.boolean(),\n});\n\nexport type App = z.infer<typeof App> & { user: User };\n\n/**\n * @returns The list of Gadget applications the current user has access to.\n */\nexport const getApps = async (user: User): Promise<App[]> => {\n const cookie = loadCookie();\n if (!cookie) {\n return [];\n }\n\n const json = await http({\n url: `https://${config.domains.services}/auth/api/apps`,\n headers: { cookie },\n responseType: \"json\",\n resolveBodyOnly: true,\n });\n\n return _.map(z.array(App).parse(json), (app) => ({ ...app, user }));\n};\n"],"names":["_","z","config","http","loadCookie","App","object","id","union","string","number","bigint","slug","primaryDomain","hasSplitEnvironments","boolean","getApps","user","cookie","json","url","domains","services","headers","responseType","resolveBodyOnly","map","array","parse","app"],"mappings":"AAAA,OAAOA,OAAO,SAAS;AACvB,SAASC,CAAC,QAAQ,MAAM;AACxB,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,IAAI,EAAEC,UAAU,QAAQ,YAAY;AAG7C,OAAO,MAAMC,MAAMJ,EAAEK,MAAM,CAAC;IAC1BC,IAAIN,EAAEO,KAAK,CAAC;QAACP,EAAEQ,MAAM;QAAIR,EAAES,MAAM;QAAIT,EAAEU,MAAM;KAAG;IAChDC,MAAMX,EAAEQ,MAAM;IACdI,eAAeZ,EAAEQ,MAAM;IACvBK,sBAAsBb,EAAEc,OAAO;AACjC,GAAG;AAIH;;CAEC,GACD,OAAO,MAAMC,UAAU,OAAOC;IAC5B,MAAMC,SAASd;IACf,IAAI,CAACc,QAAQ;QACX,OAAO,EAAE;IACX;IAEA,MAAMC,OAAO,MAAMhB,KAAK;QACtBiB,KAAK,CAAC,QAAQ,EAAElB,OAAOmB,OAAO,CAACC,QAAQ,CAAC,cAAc,CAAC;QACvDC,SAAS;YAAEL;QAAO;QAClBM,cAAc;QACdC,iBAAiB;IACnB;IAEA,OAAOzB,EAAE0B,GAAG,CAACzB,EAAE0B,KAAK,CAACtB,KAAKuB,KAAK,CAACT,OAAO,CAACU,MAAS,CAAA;YAAE,GAAGA,GAAG;YAAEZ;QAAK,CAAA;AAClE,EAAE"}
1
+ {"version":3,"sources":["../../src/services/app.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { config } from \"./config.js\";\nimport { http, loadCookie } from \"./http.js\";\nimport type { User } from \"./user.js\";\n\nexport const App = z.object({\n id: z.union([z.string(), z.number(), z.bigint()]),\n slug: z.string(),\n primaryDomain: z.string(),\n hasSplitEnvironments: z.boolean(),\n});\n\nexport type App = z.infer<typeof App> & { user: User };\n\n/**\n * @returns The list of Gadget applications the current user has access to.\n */\nexport const getApps = async (user: User): Promise<App[]> => {\n const cookie = loadCookie();\n if (!cookie) {\n return [];\n }\n\n const json = await http({\n url: `https://${config.domains.services}/auth/api/apps`,\n headers: { cookie },\n responseType: \"json\",\n resolveBodyOnly: true,\n });\n\n return z\n .array(App)\n .parse(json)\n .map((app) => ({ ...app, user }));\n};\n"],"names":["z","config","http","loadCookie","App","object","id","union","string","number","bigint","slug","primaryDomain","hasSplitEnvironments","boolean","getApps","user","cookie","json","url","domains","services","headers","responseType","resolveBodyOnly","array","parse","map","app"],"mappings":"AAAA,SAASA,CAAC,QAAQ,MAAM;AACxB,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,IAAI,EAAEC,UAAU,QAAQ,YAAY;AAG7C,OAAO,MAAMC,MAAMJ,EAAEK,MAAM,CAAC;IAC1BC,IAAIN,EAAEO,KAAK,CAAC;QAACP,EAAEQ,MAAM;QAAIR,EAAES,MAAM;QAAIT,EAAEU,MAAM;KAAG;IAChDC,MAAMX,EAAEQ,MAAM;IACdI,eAAeZ,EAAEQ,MAAM;IACvBK,sBAAsBb,EAAEc,OAAO;AACjC,GAAG;AAIH;;CAEC,GACD,OAAO,MAAMC,UAAU,OAAOC;IAC5B,MAAMC,SAASd;IACf,IAAI,CAACc,QAAQ;QACX,OAAO,EAAE;IACX;IAEA,MAAMC,OAAO,MAAMhB,KAAK;QACtBiB,KAAK,CAAC,QAAQ,EAAElB,OAAOmB,OAAO,CAACC,QAAQ,CAAC,cAAc,CAAC;QACvDC,SAAS;YAAEL;QAAO;QAClBM,cAAc;QACdC,iBAAiB;IACnB;IAEA,OAAOxB,EACJyB,KAAK,CAACrB,KACNsB,KAAK,CAACR,MACNS,GAAG,CAAC,CAACC,MAAS,CAAA;YAAE,GAAGA,GAAG;YAAEZ;QAAK,CAAA;AAClC,EAAE"}
@@ -1,11 +1,11 @@
1
- import _ from "lodash";
2
1
  import { ArgError } from "./errors.js";
3
2
  import { sprint } from "./output.js";
4
3
  export const parseBoolean = (value)=>{
5
- return _.includes([
4
+ value ??= "";
5
+ return [
6
6
  "true",
7
7
  "1"
8
- ], _.toLower(_.trim(value ?? "")));
8
+ ].includes(value.trim().toLowerCase());
9
9
  };
10
10
  export const AppArg = (value, name)=>{
11
11
  const slug = RegExp("^(https:\\/\\/)?(?<slug>[\\w-]+?)(--development)?(\\..*)?$").exec(value)?.groups?.["slug"];
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/args.ts"],"sourcesContent":["import _ from \"lodash\";\nimport { ArgError } from \"./errors.js\";\nimport { sprint } from \"./output.js\";\n\nexport const parseBoolean = (value: string | null | undefined) => {\n return _.includes([\"true\", \"1\"], _.toLower(_.trim(value ?? \"\")));\n};\n\nexport const AppArg = (value: string, name: string) => {\n const slug = /^(https:\\/\\/)?(?<slug>[\\w-]+?)(--development)?(\\..*)?$/.exec(value)?.groups?.[\"slug\"];\n if (slug) {\n return slug;\n }\n\n throw new ArgError(\n sprint`\n The ${name} option must be the application's slug or URL\n\n Examples:\n\n --${name} my-app\n --${name} my-app.gadget.app\n --${name} https://my-app.gadget.app\n --${name} https://my-app.gadget.app/edit\n --${name} https://my-app--development.gadget.app/edit\n `,\n );\n};\n"],"names":["_","ArgError","sprint","parseBoolean","value","includes","toLower","trim","AppArg","name","slug","exec","groups"],"mappings":"AAAA,OAAOA,OAAO,SAAS;AACvB,SAASC,QAAQ,QAAQ,cAAc;AACvC,SAASC,MAAM,QAAQ,cAAc;AAErC,OAAO,MAAMC,eAAe,CAACC;IAC3B,OAAOJ,EAAEK,QAAQ,CAAC;QAAC;QAAQ;KAAI,EAAEL,EAAEM,OAAO,CAACN,EAAEO,IAAI,CAACH,SAAS;AAC7D,EAAE;AAEF,OAAO,MAAMI,SAAS,CAACJ,OAAeK;IACpC,MAAMC,OAAO,qEAAyDC,IAAI,CAACP,QAAQQ,QAAQ,CAAC,OAAO;IACnG,IAAIF,MAAM;QACR,OAAOA;IACT;IAEA,MAAM,IAAIT,SACRC,MAAM,CAAC;UACD,EAAEO,KAAK;;;;UAIP,EAAEA,KAAK;UACP,EAAEA,KAAK;UACP,EAAEA,KAAK;UACP,EAAEA,KAAK;UACP,EAAEA,KAAK;IACb,CAAC;AAEL,EAAE"}
1
+ {"version":3,"sources":["../../src/services/args.ts"],"sourcesContent":["import { ArgError } from \"./errors.js\";\nimport { sprint } from \"./output.js\";\n\nexport const parseBoolean = (value: string | null | undefined) => {\n value ??= \"\";\n return [\"true\", \"1\"].includes(value.trim().toLowerCase());\n};\n\nexport const AppArg = (value: string, name: string) => {\n const slug = /^(https:\\/\\/)?(?<slug>[\\w-]+?)(--development)?(\\..*)?$/.exec(value)?.groups?.[\"slug\"];\n if (slug) {\n return slug;\n }\n\n throw new ArgError(\n sprint`\n The ${name} option must be the application's slug or URL\n\n Examples:\n\n --${name} my-app\n --${name} my-app.gadget.app\n --${name} https://my-app.gadget.app\n --${name} https://my-app.gadget.app/edit\n --${name} https://my-app--development.gadget.app/edit\n `,\n );\n};\n"],"names":["ArgError","sprint","parseBoolean","value","includes","trim","toLowerCase","AppArg","name","slug","exec","groups"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,cAAc;AACvC,SAASC,MAAM,QAAQ,cAAc;AAErC,OAAO,MAAMC,eAAe,CAACC;IAC3BA,UAAU;IACV,OAAO;QAAC;QAAQ;KAAI,CAACC,QAAQ,CAACD,MAAME,IAAI,GAAGC,WAAW;AACxD,EAAE;AAEF,OAAO,MAAMC,SAAS,CAACJ,OAAeK;IACpC,MAAMC,OAAO,qEAAyDC,IAAI,CAACP,QAAQQ,QAAQ,CAAC,OAAO;IACnG,IAAIF,MAAM;QACR,OAAOA;IACT;IAEA,MAAM,IAAIT,SACRC,MAAM,CAAC;UACD,EAAEO,KAAK;;;;UAIP,EAAEA,KAAK;UACP,EAAEA,KAAK;UACP,EAAEA,KAAK;UACP,EAAEA,KAAK;UACP,EAAEA,KAAK;IACb,CAAC;AAEL,EAAE"}
@@ -0,0 +1,17 @@
1
+ export const compact = (array)=>{
2
+ return array.filter((value)=>Boolean(value));
3
+ };
4
+ export const uniq = (array)=>{
5
+ return [
6
+ ...new Set(array)
7
+ ];
8
+ };
9
+ export const pick = (object, keys)=>{
10
+ const final = {};
11
+ for (const key of keys){
12
+ final[key] = object[key];
13
+ }
14
+ return final;
15
+ };
16
+
17
+ //# sourceMappingURL=collections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/services/collections.ts"],"sourcesContent":["export const compact = (array: unknown[]) => {\n return array.filter((value) => Boolean(value));\n};\n\nexport const uniq = <T>(array: T[]) => {\n return [...new Set(array)];\n};\n\nexport const pick = <T extends Record<string, unknown>, K extends keyof T>(object: T, keys: K[]) => {\n const final = {} as Pick<T, K>;\n for (const key of keys) {\n final[key] = object[key];\n }\n return final;\n};\n"],"names":["compact","array","filter","value","Boolean","uniq","Set","pick","object","keys","final","key"],"mappings":"AAAA,OAAO,MAAMA,UAAU,CAACC;IACtB,OAAOA,MAAMC,MAAM,CAAC,CAACC,QAAUC,QAAQD;AACzC,EAAE;AAEF,OAAO,MAAME,OAAO,CAAIJ;IACtB,OAAO;WAAI,IAAIK,IAAIL;KAAO;AAC5B,EAAE;AAEF,OAAO,MAAMM,OAAO,CAAuDC,QAAWC;IACpF,MAAMC,QAAQ,CAAC;IACf,KAAK,MAAMC,OAAOF,KAAM;QACtBC,KAAK,CAACC,IAAI,GAAGH,MAAM,CAACG,IAAI;IAC1B;IACA,OAAOD;AACT,EAAE"}
@@ -1,6 +1,5 @@
1
1
  import fs from "fs-extra";
2
2
  import isWsl from "is-wsl";
3
- import _ from "lodash";
4
3
  import os from "node:os";
5
4
  import path from "node:path";
6
5
  import process from "node:process";
@@ -23,10 +22,10 @@ normalizePackageData(pkgJson, true);
23
22
  return !this.developmentOrTestLike;
24
23
  },
25
24
  get developmentLike () {
26
- return _.startsWith(this.value, "development");
25
+ return this.value.startsWith("development");
27
26
  },
28
27
  get testLike () {
29
- return _.startsWith(this.value, "test");
28
+ return this.value.startsWith("test");
30
29
  },
31
30
  get developmentOrTestLike () {
32
31
  return this.developmentLike || this.testLike;
@@ -57,12 +56,12 @@ export const config = {
57
56
  return process.platform === "darwin";
58
57
  },
59
58
  get shell () {
60
- const SHELL = process.env["SHELL"] ?? _.split(os.userInfo().shell, path.sep).pop();
59
+ const SHELL = process.env["SHELL"] ?? os.userInfo().shell?.split(path.sep).pop();
61
60
  if (SHELL) {
62
- return _.split(SHELL, "/").at(-1);
61
+ return SHELL.split("/").at(-1);
63
62
  }
64
63
  if (this.windows && process.env["COMSPEC"]) {
65
- return _.split(process.env["COMSPEC"], /\\|\//).at(-1);
64
+ return process.env["COMSPEC"].split(/\\|\//).at(-1);
66
65
  }
67
66
  return "unknown";
68
67
  },