@gadgetinc/ggt 0.3.3 → 0.4.1

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 (148) hide show
  1. package/README.md +139 -76
  2. package/bin/dev.js +4 -7
  3. package/lib/__generated__/graphql.js.map +1 -1
  4. package/lib/commands/deploy.js +232 -0
  5. package/lib/commands/deploy.js.map +1 -0
  6. package/lib/commands/list.js +20 -16
  7. package/lib/commands/list.js.map +1 -1
  8. package/lib/commands/login.js +22 -20
  9. package/lib/commands/login.js.map +1 -1
  10. package/lib/commands/logout.js +13 -9
  11. package/lib/commands/logout.js.map +1 -1
  12. package/lib/commands/root.js +89 -56
  13. package/lib/commands/root.js.map +1 -1
  14. package/lib/commands/sync.js +253 -496
  15. package/lib/commands/sync.js.map +1 -1
  16. package/lib/commands/version.js +21 -0
  17. package/lib/commands/version.js.map +1 -0
  18. package/lib/commands/whoami.js +15 -11
  19. package/lib/commands/whoami.js.map +1 -1
  20. package/lib/main.js +4 -10
  21. package/lib/main.js.map +1 -1
  22. package/lib/services/{app.js → app/app.js} +8 -3
  23. package/lib/services/app/app.js.map +1 -0
  24. package/lib/services/app/arg.js +28 -0
  25. package/lib/services/app/arg.js.map +1 -0
  26. package/lib/services/app/edit-graphql.js +389 -0
  27. package/lib/services/app/edit-graphql.js.map +1 -0
  28. package/lib/services/command/arg.js +53 -0
  29. package/lib/services/command/arg.js.map +1 -0
  30. package/lib/services/command/command.js +27 -0
  31. package/lib/services/command/command.js.map +1 -0
  32. package/lib/services/command/context.js +60 -0
  33. package/lib/services/command/context.js.map +1 -0
  34. package/lib/services/{config.js → config/config.js} +29 -31
  35. package/lib/services/config/config.js.map +1 -0
  36. package/lib/services/config/env.js +22 -0
  37. package/lib/services/config/env.js.map +1 -0
  38. package/lib/services/config/package-json.js +9 -0
  39. package/lib/services/config/package-json.js.map +1 -0
  40. package/lib/services/filesync/changes.js +97 -0
  41. package/lib/services/filesync/changes.js.map +1 -0
  42. package/lib/services/filesync/conflicts.js +137 -0
  43. package/lib/services/filesync/conflicts.js.map +1 -0
  44. package/lib/services/filesync/directory.js +253 -0
  45. package/lib/services/filesync/directory.js.map +1 -0
  46. package/lib/services/filesync/error.js +67 -0
  47. package/lib/services/filesync/error.js.map +1 -0
  48. package/lib/services/filesync/file.js +3 -0
  49. package/lib/services/filesync/file.js.map +1 -0
  50. package/lib/services/filesync/filesync.js +673 -0
  51. package/lib/services/filesync/filesync.js.map +1 -0
  52. package/lib/services/filesync/hashes.js +150 -0
  53. package/lib/services/filesync/hashes.js.map +1 -0
  54. package/lib/services/http/auth.js +41 -0
  55. package/lib/services/http/auth.js.map +1 -0
  56. package/lib/services/http/http.js +64 -0
  57. package/lib/services/http/http.js.map +1 -0
  58. package/lib/services/output/log/field.js +3 -0
  59. package/lib/services/output/log/field.js.map +1 -0
  60. package/lib/services/output/log/format/format.js +8 -0
  61. package/lib/services/output/log/format/format.js.map +1 -0
  62. package/lib/services/output/log/format/json.js +45 -0
  63. package/lib/services/output/log/format/json.js.map +1 -0
  64. package/lib/services/output/log/format/pretty.js +147 -0
  65. package/lib/services/output/log/format/pretty.js.map +1 -0
  66. package/lib/services/output/log/level.js +41 -0
  67. package/lib/services/output/log/level.js.map +1 -0
  68. package/lib/services/output/log/logger.js +40 -0
  69. package/lib/services/output/log/logger.js.map +1 -0
  70. package/lib/services/output/log/printer.js +120 -0
  71. package/lib/services/output/log/printer.js.map +1 -0
  72. package/lib/services/output/log/structured.js +52 -0
  73. package/lib/services/output/log/structured.js.map +1 -0
  74. package/lib/services/{notify.js → output/notify.js} +7 -6
  75. package/lib/services/output/notify.js.map +1 -0
  76. package/lib/services/output/prompt.js +52 -0
  77. package/lib/services/output/prompt.js.map +1 -0
  78. package/lib/services/output/report.js +162 -0
  79. package/lib/services/output/report.js.map +1 -0
  80. package/lib/services/output/sprint.js +21 -0
  81. package/lib/services/output/sprint.js.map +1 -0
  82. package/lib/services/output/stream.js +54 -0
  83. package/lib/services/output/stream.js.map +1 -0
  84. package/lib/services/{version.js → output/update.js} +24 -16
  85. package/lib/services/output/update.js.map +1 -0
  86. package/lib/services/user/session.js +50 -0
  87. package/lib/services/user/session.js.map +1 -0
  88. package/lib/services/{user.js → user/user.js} +23 -14
  89. package/lib/services/user/user.js.map +1 -0
  90. package/lib/services/util/boolean.js +15 -0
  91. package/lib/services/util/boolean.js.map +1 -0
  92. package/lib/services/util/collection.js +38 -0
  93. package/lib/services/util/collection.js.map +1 -0
  94. package/lib/services/util/function.js +97 -0
  95. package/lib/services/util/function.js.map +1 -0
  96. package/lib/services/{is.js → util/is.js} +7 -0
  97. package/lib/services/util/is.js.map +1 -0
  98. package/lib/services/util/number.js +27 -0
  99. package/lib/services/util/number.js.map +1 -0
  100. package/lib/services/util/object.js +101 -0
  101. package/lib/services/util/object.js.map +1 -0
  102. package/lib/services/util/paths.js +36 -0
  103. package/lib/services/util/paths.js.map +1 -0
  104. package/lib/services/{promise.js → util/promise.js} +5 -7
  105. package/lib/services/util/promise.js.map +1 -0
  106. package/npm-shrinkwrap.json +2143 -1304
  107. package/package.json +50 -42
  108. package/lib/commands/index.js +0 -9
  109. package/lib/commands/index.js.map +0 -1
  110. package/lib/services/app.js.map +0 -1
  111. package/lib/services/args.js +0 -28
  112. package/lib/services/args.js.map +0 -1
  113. package/lib/services/collections.js +0 -17
  114. package/lib/services/collections.js.map +0 -1
  115. package/lib/services/config.js.map +0 -1
  116. package/lib/services/debounce.js +0 -21
  117. package/lib/services/debounce.js.map +0 -1
  118. package/lib/services/defaults.js +0 -8
  119. package/lib/services/defaults.js.map +0 -1
  120. package/lib/services/edit-graphql.js +0 -202
  121. package/lib/services/edit-graphql.js.map +0 -1
  122. package/lib/services/errors.js +0 -277
  123. package/lib/services/errors.js.map +0 -1
  124. package/lib/services/filesync.js +0 -404
  125. package/lib/services/filesync.js.map +0 -1
  126. package/lib/services/fs.js +0 -35
  127. package/lib/services/fs.js.map +0 -1
  128. package/lib/services/http.js +0 -53
  129. package/lib/services/http.js.map +0 -1
  130. package/lib/services/is.js.map +0 -1
  131. package/lib/services/log.js +0 -45
  132. package/lib/services/log.js.map +0 -1
  133. package/lib/services/noop.js +0 -4
  134. package/lib/services/noop.js.map +0 -1
  135. package/lib/services/notify.js.map +0 -1
  136. package/lib/services/output.js +0 -74
  137. package/lib/services/output.js.map +0 -1
  138. package/lib/services/promise.js.map +0 -1
  139. package/lib/services/prompt.js +0 -22
  140. package/lib/services/prompt.js.map +0 -1
  141. package/lib/services/session.js +0 -31
  142. package/lib/services/session.js.map +0 -1
  143. package/lib/services/sleep.js +0 -21
  144. package/lib/services/sleep.js.map +0 -1
  145. package/lib/services/timeout.js +0 -8
  146. package/lib/services/timeout.js.map +0 -1
  147. package/lib/services/user.js.map +0 -1
  148. package/lib/services/version.js.map +0 -1
@@ -1,404 +0,0 @@
1
- import { _ as _define_property } from "@swc/helpers/_/_define_property";
2
- import chalkTemplate from "chalk-template";
3
- import { findUp } from "find-up";
4
- import fs from "fs-extra";
5
- import ignore from "ignore";
6
- import ms from "ms";
7
- import path from "node:path";
8
- import process from "node:process";
9
- import normalizePath from "normalize-path";
10
- import pMap from "p-map";
11
- import pRetry from "p-retry";
12
- import pluralize from "pluralize";
13
- import { dedent } from "ts-dedent";
14
- import { z } from "zod";
15
- import { getApps } from "./app.js";
16
- import { config } from "./config.js";
17
- import { ArgError, InvalidSyncFileError } from "./errors.js";
18
- import { isEmptyOrNonExistentDir, swallowEnoent } from "./fs.js";
19
- import { createLogger } from "./log.js";
20
- import { noop } from "./noop.js";
21
- import { println, sortByLevenshtein, sprint } from "./output.js";
22
- import { select } from "./prompt.js";
23
- const log = createLogger("filesync");
24
- export const ALWAYS_IGNORE_PATHS = [
25
- ".DS_Store",
26
- "node_modules",
27
- ".git"
28
- ];
29
- export class FileSync {
30
- /**
31
- * The last filesVersion that was written to the filesystem.
32
- *
33
- * This determines if the filesystem in Gadget is ahead of the
34
- * filesystem on the local machine.
35
- */ get filesVersion() {
36
- return BigInt(this._state.filesVersion);
37
- }
38
- /**
39
- * The largest mtime that was seen on the filesystem.
40
- *
41
- * This is used to determine if any files have changed since the last
42
- * sync. This does not include the mtime of files that are ignored.
43
- */ get mtime() {
44
- return this._state.mtime;
45
- }
46
- /**
47
- * Initializes a {@linkcode FileSync} instance.
48
- * - Ensures the directory exists.
49
- * - Ensures the directory is empty or contains a `.gadget/sync.json` file (unless `options.force` is `true`)
50
- * - Ensures an app is specified (either via `options.app` or by prompting the user)
51
- * - Ensures the specified app matches the app the directory was previously synced to (unless `options.force` is `true`)
52
- */ static async init(user, options) {
53
- const apps = await getApps(user);
54
- if (apps.length === 0) {
55
- throw new ArgError(sprint`
56
- You (${user.email}) don't have have any Gadget applications.
57
-
58
- Visit https://gadget.new to create one!
59
- `);
60
- }
61
- let dir = options.dir;
62
- if (!dir) {
63
- // the user didn't specify a directory
64
- const filepath = await findUp(".gadget/sync.json");
65
- if (filepath) {
66
- // we found a .gadget/sync.json file, use its parent directory
67
- dir = path.join(filepath, "../..");
68
- } else {
69
- // we didn't find a .gadget/sync.json file, use the current directory
70
- dir = process.cwd();
71
- }
72
- }
73
- if (config.windows && dir.startsWith("~/")) {
74
- // `~` doesn't expand to the home directory on Windows
75
- dir = path.join(config.homeDir, dir.slice(2));
76
- }
77
- // ensure the root directory is an absolute path and exists
78
- await fs.ensureDir(dir = path.resolve(dir));
79
- // try to load the .gadget/sync.json file
80
- const state = await fs.readJson(path.join(dir, ".gadget/sync.json")).then((json)=>z.object({
81
- app: z.string(),
82
- filesVersion: z.string(),
83
- mtime: z.number()
84
- }).parse(json)).catch(noop);
85
- let appSlug = options.app || state?.app;
86
- if (!appSlug) {
87
- // the user didn't specify an app, suggest some apps that they can sync to
88
- appSlug = await select({
89
- message: "Please select the app to sync to.",
90
- choices: apps.map((x)=>x.slug)
91
- });
92
- }
93
- // try to find the appSlug in their list of apps
94
- const app = apps.find((app)=>app.slug === appSlug);
95
- if (!app) {
96
- // the specified appSlug doesn't exist in their list of apps,
97
- // either they misspelled it or they don't have access to it
98
- // anymore, suggest some apps that are similar to the one they
99
- // specified
100
- const similarAppSlugs = sortByLevenshtein(appSlug, apps.map((app)=>app.slug)).slice(0, 5);
101
- throw new ArgError(sprint`
102
- Unknown application:
103
-
104
- ${appSlug}
105
-
106
- Did you mean one of these?
107
-
108
-
109
- `.concat(` • ${similarAppSlugs.join("\n • ")}`));
110
- }
111
- const ignore = options.extraIgnorePaths ?? [];
112
- if (!state) {
113
- // the .gadget/sync.json file didn't exist or contained invalid json
114
- if (await isEmptyOrNonExistentDir(dir) || options.force) {
115
- // the directory is empty or the user passed --force
116
- // either way, create a fresh .gadget/sync.json file
117
- return new FileSync(dir, app, ignore, {
118
- app: app.slug,
119
- filesVersion: "0",
120
- mtime: 0
121
- });
122
- }
123
- // the directory isn't empty and the user didn't pass --force
124
- throw new InvalidSyncFileError(dir, app.slug);
125
- }
126
- // the .gadget/sync.json file exists
127
- if (state.app === app.slug) {
128
- // the .gadget/sync.json file is for the same app that the user specified
129
- return new FileSync(dir, app, ignore, state);
130
- }
131
- // the .gadget/sync.json file is for a different app
132
- if (options.force) {
133
- // the user passed --force, so use the app they specified and overwrite everything
134
- return new FileSync(dir, app, ignore, {
135
- app: app.slug,
136
- filesVersion: "0",
137
- mtime: 0
138
- });
139
- }
140
- // the user didn't pass --force, so throw an error
141
- throw new ArgError(sprint`
142
- You were about to sync the following app to the following directory:
143
-
144
- {dim ${app.slug}} → {dim ${dir}}
145
-
146
- However, that directory has already been synced with this app:
147
-
148
- {dim ${state.app}}
149
-
150
- If you're sure that you want to sync:
151
-
152
- {dim ${app.slug}} → {dim ${dir}}
153
-
154
- Then run {dim ggt sync} again with the {dim --force} flag.
155
- `);
156
- }
157
- /**
158
- * Converts an absolute path into a relative one from {@linkcode dir}.
159
- */ relative(to) {
160
- if (!path.isAbsolute(to)) {
161
- // the filepath is already relative
162
- return to;
163
- }
164
- return path.relative(this.dir, to);
165
- }
166
- /**
167
- * Converts a relative path into an absolute one from {@linkcode dir}.
168
- */ absolute(...pathSegments) {
169
- return path.resolve(this.dir, ...pathSegments);
170
- }
171
- /**
172
- * Similar to {@linkcode relative} in that it converts an absolute
173
- * path into a relative one from {@linkcode dir}. However, it also
174
- * changes any slashes to be posix/unix-like forward slashes,
175
- * condenses repeated slashes into a single slash, and adds a trailing
176
- * slash if the path is a directory.
177
- *
178
- * This is used when sending file-sync events to Gadget to ensure that
179
- * the paths are consistent across platforms.
180
- *
181
- * @see https://www.npmjs.com/package/normalize-path
182
- */ normalize(filepath, isDirectory) {
183
- return normalizePath(path.isAbsolute(filepath) ? this.relative(filepath) : filepath) + (isDirectory ? "/" : "");
184
- }
185
- /**
186
- * Reloads the ignore rules from the `.ignore` file.
187
- */ reloadIgnorePaths() {
188
- this._ignorer = ignore.default();
189
- this._ignorer.add([
190
- ...ALWAYS_IGNORE_PATHS,
191
- ...this._extraIgnorePaths
192
- ]);
193
- try {
194
- const content = fs.readFileSync(this.absolute(".ignore"), "utf-8");
195
- this._ignorer.add(content);
196
- log.info("reloaded ignore rules");
197
- } catch (error) {
198
- swallowEnoent(error);
199
- }
200
- }
201
- /**
202
- * Returns `true` if the {@linkcode filepath} should be ignored.
203
- */ ignores(filepath) {
204
- const relative = this.relative(filepath);
205
- if (relative === "") {
206
- // don't ignore the root dir
207
- return false;
208
- }
209
- if (relative.startsWith("..")) {
210
- // anything above the root dir is ignored
211
- return true;
212
- }
213
- return this._ignorer.ignores(relative);
214
- }
215
- /**
216
- * Walks the directory and yields each file and directory.
217
- *
218
- * If a directory is empty, or only contains ignored entries, it will
219
- * be yielded as a directory. Otherwise, each file within the
220
- * directory will be yielded.
221
- */ async *walkDir({ dir = this.dir, skipIgnored = true } = {}) {
222
- // track whether the directory has any entries (ignored entries don't count)
223
- let hasEntries = false;
224
- for await (const entry of (await fs.opendir(dir))){
225
- const filepath = path.join(dir, entry.name);
226
- if (skipIgnored && this.ignores(filepath)) {
227
- continue;
228
- }
229
- hasEntries = true;
230
- if (entry.isDirectory()) {
231
- yield* this.walkDir({
232
- dir: filepath,
233
- skipIgnored
234
- });
235
- } else if (entry.isFile()) {
236
- yield [
237
- filepath,
238
- await fs.stat(filepath)
239
- ];
240
- }
241
- }
242
- if (!hasEntries) {
243
- // if the directory is empty, or only contains ignored entries, yield it as a directory
244
- yield [
245
- `${dir}/`,
246
- await fs.stat(dir)
247
- ];
248
- }
249
- }
250
- /**
251
- * Writes the {@linkcode changed} and {@linkcode deleted} files to the filesystem.
252
- * @param filesVersion The files version associated with the files that are being written.
253
- * @param changed The files that have changed.
254
- * @param deleted The paths that have been deleted.
255
- * @param force If `true`, the files version will be updated even if it's less than the current files version.
256
- */ async write(filesVersion, changed, deleted, force = false) {
257
- filesVersion = BigInt(filesVersion);
258
- await pMap(deleted, async (filepath)=>{
259
- const currentPath = this.absolute(filepath);
260
- const backupPath = this.absolute(".gadget/backup", this.relative(filepath));
261
- // rather than `rm -rf`ing files, we move them to
262
- // `.gadget/backup/` so that users can recover them if something
263
- // goes wrong. We've seen a lot of EBUSY/EINVAL errors when moving
264
- // files so we retry a few times.
265
- await pRetry(async ()=>{
266
- try {
267
- // remove the current backup file in case it exists and is a
268
- // different type (file vs directory)
269
- await fs.remove(backupPath);
270
- await fs.move(currentPath, backupPath);
271
- } catch (error) {
272
- // replicate the behavior of `rm -rf` and ignore ENOENT
273
- swallowEnoent(error);
274
- }
275
- }, {
276
- retries: 2,
277
- minTimeout: ms("100ms"),
278
- onFailedAttempt: (error)=>{
279
- log.warn("failed to move file to backup", {
280
- error
281
- });
282
- }
283
- });
284
- });
285
- await pMap(changed, async (file)=>{
286
- const absolutePath = this.absolute(file.path);
287
- if (file.path.endsWith("/")) {
288
- await fs.ensureDir(absolutePath, {
289
- mode: 0o755
290
- });
291
- return;
292
- }
293
- await fs.ensureDir(path.dirname(absolutePath), {
294
- mode: 0o755
295
- });
296
- await fs.writeFile(absolutePath, Buffer.from(file.content, file.encoding), {
297
- mode: file.mode
298
- });
299
- if (absolutePath === this.absolute(".ignore")) {
300
- this.reloadIgnorePaths();
301
- }
302
- });
303
- this._state.mtime = Date.now();
304
- if (filesVersion > BigInt(this._state.filesVersion) || force) {
305
- this._state.filesVersion = String(filesVersion);
306
- }
307
- this._save();
308
- log.info("wrote", {
309
- ...this._state,
310
- changed: Array.from(changed).map((x)=>x.path),
311
- deleted: Array.from(deleted)
312
- });
313
- }
314
- /**
315
- * Synchronously writes {@linkcode _state} to `.gadget/sync.json`.
316
- */ _save() {
317
- fs.outputJSONSync(this.absolute(".gadget/sync.json"), this._state, {
318
- spaces: 2
319
- });
320
- }
321
- constructor(/**
322
- * An absolute path to the directory that is being synced.
323
- */ dir, /**
324
- * The Gadget application this filesystem is synced to.
325
- */ app, /**
326
- * Additional paths to ignore when syncing the filesystem.
327
- */ _extraIgnorePaths, /**
328
- * The state of the filesystem.
329
- *
330
- * This is persisted to `.gadget/sync.json`.
331
- */ _state){
332
- _define_property(this, "dir", void 0);
333
- _define_property(this, "app", void 0);
334
- _define_property(this, "_extraIgnorePaths", void 0);
335
- _define_property(this, "_state", void 0);
336
- /**
337
- * The {@linkcode Ignore} instance that is used to determine if a file
338
- * should be ignored.
339
- *
340
- * @see https://www.npmjs.com/package/ignore
341
- */ _define_property(this, "_ignorer", void 0);
342
- this.dir = dir;
343
- this.app = app;
344
- this._extraIgnorePaths = _extraIgnorePaths;
345
- this._state = _state;
346
- this._save();
347
- this.reloadIgnorePaths();
348
- }
349
- }
350
- /**
351
- * Pretty-prints changed and deleted filepaths to the console.
352
- *
353
- * @param prefix The prefix to print before each line.
354
- * @param changed The normalized paths that have changed.
355
- * @param deleted The normalized paths that have been deleted.
356
- * @param options.limit The maximum number of lines to print. Defaults to 10.
357
- */ export const printPaths = (prefix, changed, deleted, { limit = 10 } = {})=>{
358
- const lines = [
359
- ...changed.map((normalizedPath)=>chalkTemplate`{green ${prefix}} ${normalizedPath} {gray (changed)}`),
360
- ...deleted.map((normalizedPath)=>chalkTemplate`{red ${prefix}} ${normalizedPath} {gray (deleted)}`)
361
- ].sort((a, b)=>a.slice(a.indexOf(" ") + 1).localeCompare(b.slice(b.indexOf(" ") + 1)));
362
- let logged = 0;
363
- for (const line of lines){
364
- println(line);
365
- if (++logged === limit) {
366
- break;
367
- }
368
- }
369
- if (lines.length > logged) {
370
- println`{gray … ${lines.length - logged} more}`;
371
- }
372
- println`{gray ${pluralize("file", lines.length, true)} in total. ${changed.length} changed, ${deleted.length} deleted.}`;
373
- println();
374
- };
375
- export const REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION = dedent(/* GraphQL */ `
376
- subscription RemoteFileSyncEvents($localFilesVersion: String!) {
377
- remoteFileSyncEvents(localFilesVersion: $localFilesVersion, encoding: base64) {
378
- remoteFilesVersion
379
- changed {
380
- path
381
- mode
382
- content
383
- encoding
384
- }
385
- deleted {
386
- path
387
- }
388
- }
389
- }
390
- `);
391
- export const REMOTE_FILES_VERSION_QUERY = dedent(/* GraphQL */ `
392
- query RemoteFilesVersion {
393
- remoteFilesVersion
394
- }
395
- `);
396
- export const PUBLISH_FILE_SYNC_EVENTS_MUTATION = dedent(/* GraphQL */ `
397
- mutation PublishFileSyncEvents($input: PublishFileSyncEventsInput!) {
398
- publishFileSyncEvents(input: $input) {
399
- remoteFilesVersion
400
- }
401
- }
402
- `);
403
-
404
- //# sourceMappingURL=filesync.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/filesync.ts"],"sourcesContent":["import chalkTemplate from \"chalk-template\";\nimport { findUp } from \"find-up\";\nimport type { Stats } from \"fs-extra\";\nimport fs from \"fs-extra\";\nimport type { Ignore } from \"ignore\";\nimport ignore from \"ignore\";\nimport ms from \"ms\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport normalizePath from \"normalize-path\";\nimport pMap from \"p-map\";\nimport pRetry from \"p-retry\";\nimport pluralize from \"pluralize\";\nimport { dedent } from \"ts-dedent\";\nimport { z } from \"zod\";\nimport type {\n PublishFileSyncEventsMutation,\n PublishFileSyncEventsMutationVariables,\n RemoteFileSyncEventsSubscription,\n RemoteFileSyncEventsSubscriptionVariables,\n RemoteFilesVersionQuery,\n RemoteFilesVersionQueryVariables,\n} from \"../__generated__/graphql.js\";\nimport type { App } from \"./app.js\";\nimport { getApps } from \"./app.js\";\nimport { config } from \"./config.js\";\nimport type { Query } from \"./edit-graphql.js\";\nimport { ArgError, InvalidSyncFileError } from \"./errors.js\";\nimport { isEmptyOrNonExistentDir, swallowEnoent } from \"./fs.js\";\nimport { createLogger } from \"./log.js\";\nimport { noop } from \"./noop.js\";\nimport { println, sortByLevenshtein, sprint } from \"./output.js\";\nimport { select } from \"./prompt.js\";\nimport type { User } from \"./user.js\";\n\nconst log = createLogger(\"filesync\");\n\nexport const ALWAYS_IGNORE_PATHS = [\".DS_Store\", \"node_modules\", \".git\"] as const;\n\ninterface File {\n path: string;\n mode: number;\n content: string;\n encoding: \"utf8\" | \"base64\";\n}\n\nexport class FileSync {\n /**\n * The {@linkcode Ignore} instance that is used to determine if a file\n * should be ignored.\n *\n * @see https://www.npmjs.com/package/ignore\n */\n private _ignorer!: Ignore;\n\n private constructor(\n /**\n * An absolute path to the directory that is being synced.\n */\n readonly dir: string,\n\n /**\n * The Gadget application this filesystem is synced to.\n */\n readonly app: App,\n\n /**\n * Additional paths to ignore when syncing the filesystem.\n */\n private _extraIgnorePaths: string[],\n\n /**\n * The state of the filesystem.\n *\n * This is persisted to `.gadget/sync.json`.\n */\n private _state: { app: string; filesVersion: string; mtime: number },\n ) {\n this._save();\n this.reloadIgnorePaths();\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() {\n return BigInt(this._state.filesVersion);\n }\n\n /**\n * The largest mtime that was seen on the filesystem.\n *\n * This is used to determine if any files have changed since the last\n * sync. This does not include the mtime of files that are ignored.\n */\n get mtime() {\n return this._state.mtime;\n }\n\n /**\n * Initializes a {@linkcode FileSync} instance.\n * - Ensures the directory exists.\n * - Ensures the directory is empty or contains a `.gadget/sync.json` file (unless `options.force` is `true`)\n * - Ensures an app is specified (either via `options.app` or by prompting the user)\n * - Ensures the specified app matches the app the directory was previously synced to (unless `options.force` is `true`)\n */\n static async init(user: User, options: { dir?: string; app?: string; force?: boolean; extraIgnorePaths?: string[] }): Promise<FileSync> {\n const apps = await getApps(user);\n if (apps.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 let dir = options.dir;\n if (!dir) {\n // the user didn't specify a directory\n const filepath = await findUp(\".gadget/sync.json\");\n if (filepath) {\n // we found a .gadget/sync.json file, use its parent directory\n dir = path.join(filepath, \"../..\");\n } else {\n // we didn't find a .gadget/sync.json file, use the current directory\n dir = process.cwd();\n }\n }\n\n if (config.windows && dir.startsWith(\"~/\")) {\n // `~` doesn't expand to the home directory on Windows\n dir = path.join(config.homeDir, dir.slice(2));\n }\n\n // ensure the root directory is an absolute path and exists\n await fs.ensureDir((dir = path.resolve(dir)));\n\n // try to load the .gadget/sync.json file\n const state = await fs\n .readJson(path.join(dir, \".gadget/sync.json\"))\n .then((json) =>\n z\n .object({\n app: z.string(),\n filesVersion: z.string(),\n mtime: z.number(),\n })\n .parse(json),\n )\n .catch(noop);\n\n let appSlug = options.app || state?.app;\n if (!appSlug) {\n // the user didn't specify an app, suggest some apps that they can sync to\n appSlug = await select({\n message: \"Please select the app to sync to.\",\n choices: apps.map((x) => x.slug),\n });\n }\n\n // try to find the appSlug in their list of apps\n const app = apps.find((app) => app.slug === appSlug);\n if (!app) {\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 = sortByLevenshtein(\n appSlug,\n apps.map((app) => app.slug),\n ).slice(0, 5);\n\n throw new ArgError(\n sprint`\n Unknown application:\n\n ${appSlug}\n\n Did you mean one of these?\n\n\n `.concat(` • ${similarAppSlugs.join(\"\\n • \")}`),\n );\n }\n\n const ignore = options.extraIgnorePaths ?? [];\n\n if (!state) {\n // the .gadget/sync.json file didn't exist or contained invalid json\n if ((await isEmptyOrNonExistentDir(dir)) || options.force) {\n // the directory is empty or the user passed --force\n // either way, create a fresh .gadget/sync.json file\n return new FileSync(dir, app, ignore, { app: app.slug, filesVersion: \"0\", mtime: 0 });\n }\n\n // the directory isn't empty and the user didn't pass --force\n throw new InvalidSyncFileError(dir, app.slug);\n }\n\n // the .gadget/sync.json file exists\n if (state.app === app.slug) {\n // the .gadget/sync.json file is for the same app that the user specified\n return new FileSync(dir, app, ignore, state);\n }\n\n // the .gadget/sync.json file is for a different app\n if (options.force) {\n // the user passed --force, so use the app they specified and overwrite everything\n return new FileSync(dir, app, ignore, { app: app.slug, filesVersion: \"0\", mtime: 0 });\n }\n\n // the user didn't pass --force, so throw an error\n throw new ArgError(sprint`\n You were about to sync the following app to the following directory:\n\n {dim ${app.slug}} → {dim ${dir}}\n\n However, that directory has already been synced with this app:\n\n {dim ${state.app}}\n\n If you're sure that you want to sync:\n\n {dim ${app.slug}} → {dim ${dir}}\n\n Then run {dim ggt sync} again with the {dim --force} flag.\n `);\n }\n\n /**\n * Converts an absolute path into a relative one from {@linkcode dir}.\n */\n relative(to: string): string {\n if (!path.isAbsolute(to)) {\n // the filepath is already relative\n return to;\n }\n\n return path.relative(this.dir, to);\n }\n\n /**\n * Converts a relative path into an absolute one from {@linkcode dir}.\n */\n absolute(...pathSegments: string[]): string {\n return path.resolve(this.dir, ...pathSegments);\n }\n\n /**\n * Similar to {@linkcode relative} in that it converts an absolute\n * path into a relative one from {@linkcode dir}. However, it also\n * changes any slashes to be posix/unix-like forward slashes,\n * condenses repeated slashes into a single slash, and adds a trailing\n * slash if the path is a directory.\n *\n * This is used when sending file-sync events to Gadget to ensure that\n * the paths are consistent across platforms.\n *\n * @see https://www.npmjs.com/package/normalize-path\n */\n normalize(filepath: string, isDirectory: boolean): string {\n return normalizePath(path.isAbsolute(filepath) ? this.relative(filepath) : filepath) + (isDirectory ? \"/\" : \"\");\n }\n\n /**\n * Reloads the ignore rules from the `.ignore` file.\n */\n reloadIgnorePaths(): void {\n this._ignorer = ignore.default();\n this._ignorer.add([...ALWAYS_IGNORE_PATHS, ...this._extraIgnorePaths]);\n\n try {\n const content = fs.readFileSync(this.absolute(\".ignore\"), \"utf-8\");\n this._ignorer.add(content);\n log.info(\"reloaded ignore rules\");\n } catch (error) {\n swallowEnoent(error);\n }\n }\n\n /**\n * Returns `true` if the {@linkcode filepath} should be ignored.\n */\n ignores(filepath: string): boolean {\n const relative = this.relative(filepath);\n if (relative === \"\") {\n // don't ignore the root dir\n return false;\n }\n\n if (relative.startsWith(\"..\")) {\n // anything above the root dir is ignored\n return true;\n }\n\n return this._ignorer.ignores(relative);\n }\n\n /**\n * Walks the directory and yields each file and directory.\n *\n * If a directory is empty, or only contains ignored entries, it will\n * be yielded as a directory. Otherwise, each file within the\n * directory will be yielded.\n */\n async *walkDir({ dir = this.dir, skipIgnored = true } = {}): AsyncGenerator<[absolutePath: string, entry: Stats]> {\n // track whether the directory has any entries (ignored entries don't count)\n let hasEntries = false;\n\n for await (const entry of await fs.opendir(dir)) {\n const filepath = path.join(dir, entry.name);\n if (skipIgnored && this.ignores(filepath)) {\n continue;\n }\n\n hasEntries = true;\n\n if (entry.isDirectory()) {\n yield* this.walkDir({ dir: filepath, skipIgnored });\n } else if (entry.isFile()) {\n yield [filepath, await fs.stat(filepath)];\n }\n }\n\n if (!hasEntries) {\n // if the directory is empty, or only contains ignored entries, yield it as a directory\n yield [`${dir}/`, await fs.stat(dir)];\n }\n }\n\n /**\n * Writes the {@linkcode changed} and {@linkcode deleted} files to the filesystem.\n * @param filesVersion The files version associated with the files that are being written.\n * @param changed The files that have changed.\n * @param deleted The paths that have been deleted.\n * @param force If `true`, the files version will be updated even if it's less than the current files version.\n */\n async write(filesVersion: bigint | string, changed: Iterable<File>, deleted: Iterable<string>, force = false): Promise<void> {\n filesVersion = BigInt(filesVersion);\n\n await pMap(deleted, async (filepath) => {\n const currentPath = this.absolute(filepath);\n const backupPath = this.absolute(\".gadget/backup\", this.relative(filepath));\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 } catch (error) {\n // replicate the behavior of `rm -rf` and ignore ENOENT\n swallowEnoent(error);\n }\n },\n {\n retries: 2,\n minTimeout: ms(\"100ms\"),\n onFailedAttempt: (error) => {\n log.warn(\"failed to move file to backup\", { error });\n },\n },\n );\n });\n\n await pMap(changed, async (file) => {\n const absolutePath = this.absolute(file.path);\n if (file.path.endsWith(\"/\")) {\n await fs.ensureDir(absolutePath, { mode: 0o755 });\n return;\n }\n\n await fs.ensureDir(path.dirname(absolutePath), { mode: 0o755 });\n await fs.writeFile(absolutePath, Buffer.from(file.content, file.encoding), { mode: file.mode });\n\n if (absolutePath === this.absolute(\".ignore\")) {\n this.reloadIgnorePaths();\n }\n });\n\n this._state.mtime = Date.now();\n if (filesVersion > BigInt(this._state.filesVersion) || force) {\n this._state.filesVersion = String(filesVersion);\n }\n\n this._save();\n\n log.info(\"wrote\", {\n ...this._state,\n changed: Array.from(changed).map((x) => x.path),\n deleted: Array.from(deleted),\n });\n }\n\n /**\n * Synchronously writes {@linkcode _state} to `.gadget/sync.json`.\n */\n private _save() {\n fs.outputJSONSync(this.absolute(\".gadget/sync.json\"), this._state, { spaces: 2 });\n }\n}\n\n/**\n * Pretty-prints changed and deleted filepaths to the console.\n *\n * @param prefix The prefix to print before each line.\n * @param changed The normalized paths that have changed.\n * @param deleted The normalized paths that have been deleted.\n * @param options.limit The maximum number of lines to print. Defaults to 10.\n */\nexport const printPaths = (prefix: string, changed: string[], deleted: string[], { limit = 10 } = {}) => {\n const lines = [\n ...changed.map((normalizedPath) => chalkTemplate`{green ${prefix}} ${normalizedPath} {gray (changed)}`),\n ...deleted.map((normalizedPath) => chalkTemplate`{red ${prefix}} ${normalizedPath} {gray (deleted)}`),\n ].sort((a, b) => a.slice(a.indexOf(\" \") + 1).localeCompare(b.slice(b.indexOf(\" \") + 1)));\n\n let logged = 0;\n for (const line of lines) {\n println(line);\n if (++logged === limit) {\n break;\n }\n }\n\n if (lines.length > logged) {\n println`{gray … ${lines.length - logged} more}`;\n }\n\n println`{gray ${pluralize(\"file\", lines.length, true)} in total. ${changed.length} changed, ${deleted.length} deleted.}`;\n println();\n};\n\nexport const REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION = dedent(/* GraphQL */ `\n subscription RemoteFileSyncEvents($localFilesVersion: String!) {\n remoteFileSyncEvents(localFilesVersion: $localFilesVersion, encoding: base64) {\n remoteFilesVersion\n changed {\n path\n mode\n content\n encoding\n }\n deleted {\n path\n }\n }\n }\n`) as Query<RemoteFileSyncEventsSubscription, RemoteFileSyncEventsSubscriptionVariables>;\n\nexport const REMOTE_FILES_VERSION_QUERY = dedent(/* GraphQL */ `\n query RemoteFilesVersion {\n remoteFilesVersion\n }\n`) as Query<RemoteFilesVersionQuery, RemoteFilesVersionQueryVariables>;\n\nexport const PUBLISH_FILE_SYNC_EVENTS_MUTATION = dedent(/* GraphQL */ `\n mutation PublishFileSyncEvents($input: PublishFileSyncEventsInput!) {\n publishFileSyncEvents(input: $input) {\n remoteFilesVersion\n }\n }\n`) as Query<PublishFileSyncEventsMutation, PublishFileSyncEventsMutationVariables>;\n"],"names":["chalkTemplate","findUp","fs","ignore","ms","path","process","normalizePath","pMap","pRetry","pluralize","dedent","z","getApps","config","ArgError","InvalidSyncFileError","isEmptyOrNonExistentDir","swallowEnoent","createLogger","noop","println","sortByLevenshtein","sprint","select","log","ALWAYS_IGNORE_PATHS","FileSync","filesVersion","BigInt","_state","mtime","init","user","options","apps","length","email","dir","filepath","join","cwd","windows","startsWith","homeDir","slice","ensureDir","resolve","state","readJson","then","json","object","app","string","number","parse","catch","appSlug","message","choices","map","x","slug","find","similarAppSlugs","concat","extraIgnorePaths","force","relative","to","isAbsolute","absolute","pathSegments","normalize","isDirectory","reloadIgnorePaths","_ignorer","default","add","_extraIgnorePaths","content","readFileSync","info","error","ignores","walkDir","skipIgnored","hasEntries","entry","opendir","name","isFile","stat","write","changed","deleted","currentPath","backupPath","remove","move","retries","minTimeout","onFailedAttempt","warn","file","absolutePath","endsWith","mode","dirname","writeFile","Buffer","from","encoding","Date","now","String","_save","Array","outputJSONSync","spaces","printPaths","prefix","limit","lines","normalizedPath","sort","a","b","indexOf","localeCompare","logged","line","REMOTE_FILE_SYNC_EVENTS_SUBSCRIPTION","REMOTE_FILES_VERSION_QUERY","PUBLISH_FILE_SYNC_EVENTS_MUTATION"],"mappings":"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"}
@@ -1,35 +0,0 @@
1
- import fs from "fs-extra";
2
- import path from "node:path";
3
- import { z } from "zod";
4
- import { createLogger } from "./log.js";
5
- const log = createLogger("fs");
6
- const enoentSchema = z.object({
7
- code: z.literal("ENOENT"),
8
- path: z.string()
9
- });
10
- export const swallowEnoent = (error)=>{
11
- try {
12
- const enoent = enoentSchema.parse(error);
13
- log.debug("swallowing enoent error", {
14
- path: path.basename(enoent.path)
15
- });
16
- return;
17
- } catch {
18
- throw error;
19
- }
20
- };
21
- export const isEmptyOrNonExistentDir = async (dir)=>{
22
- try {
23
- for await (const _ of (await fs.opendir(dir, {
24
- bufferSize: 1
25
- }))){
26
- return false;
27
- }
28
- return true;
29
- } catch (error) {
30
- swallowEnoent(error);
31
- return true;
32
- }
33
- };
34
-
35
- //# sourceMappingURL=fs.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/fs.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { createLogger } from \"./log.js\";\n\nconst log = createLogger(\"fs\");\n\nconst enoentSchema = z.object({ code: z.literal(\"ENOENT\"), path: z.string() });\n\nexport const swallowEnoent = (error: unknown): void => {\n try {\n const enoent = enoentSchema.parse(error);\n log.debug(\"swallowing enoent error\", { path: path.basename(enoent.path) });\n return;\n } catch {\n throw error;\n }\n};\n\nexport const isEmptyOrNonExistentDir = async (dir: string): Promise<boolean> => {\n try {\n for await (const _ of await fs.opendir(dir, { bufferSize: 1 })) {\n return false;\n }\n return true;\n } catch (error) {\n swallowEnoent(error);\n return true;\n }\n};\n"],"names":["fs","path","z","createLogger","log","enoentSchema","object","code","literal","string","swallowEnoent","error","enoent","parse","debug","basename","isEmptyOrNonExistentDir","dir","_","opendir","bufferSize"],"mappings":"AAAA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,YAAY;AAC7B,SAASC,CAAC,QAAQ,MAAM;AACxB,SAASC,YAAY,QAAQ,WAAW;AAExC,MAAMC,MAAMD,aAAa;AAEzB,MAAME,eAAeH,EAAEI,MAAM,CAAC;IAAEC,MAAML,EAAEM,OAAO,CAAC;IAAWP,MAAMC,EAAEO,MAAM;AAAG;AAE5E,OAAO,MAAMC,gBAAgB,CAACC;IAC5B,IAAI;QACF,MAAMC,SAASP,aAAaQ,KAAK,CAACF;QAClCP,IAAIU,KAAK,CAAC,2BAA2B;YAAEb,MAAMA,KAAKc,QAAQ,CAACH,OAAOX,IAAI;QAAE;QACxE;IACF,EAAE,OAAM;QACN,MAAMU;IACR;AACF,EAAE;AAEF,OAAO,MAAMK,0BAA0B,OAAOC;IAC5C,IAAI;QACF,WAAW,MAAMC,KAAK,CAAA,MAAMlB,GAAGmB,OAAO,CAACF,KAAK;YAAEG,YAAY;QAAE,EAAC,EAAG;YAC9D,OAAO;QACT;QACA,OAAO;IACT,EAAE,OAAOT,OAAO;QACdD,cAAcC;QACd,OAAO;IACT;AACF,EAAE"}
@@ -1,53 +0,0 @@
1
- import { got, HTTPError } from "got";
2
- import { config } from "./config.js";
3
- import { createLogger } from "./log.js";
4
- import { readSession, writeSession } from "./session.js";
5
- const log = createLogger("http");
6
- export const http = got.extend({
7
- hooks: {
8
- beforeRequest: [
9
- (options)=>{
10
- options.headers["user-agent"] = config.versionFull;
11
- log.info("http request", {
12
- method: options.method,
13
- url: options.url?.toString()
14
- });
15
- }
16
- ],
17
- afterResponse: [
18
- (response)=>{
19
- log.info("http response", {
20
- method: response.request.options.method,
21
- url: response.request.options.url?.toString(),
22
- statusCode: response.statusCode,
23
- traceId: response.headers["x-trace-id"]
24
- });
25
- if (response.statusCode === 401 && isGadgetRequest(response.request.options)) {
26
- writeSession(undefined);
27
- }
28
- return response;
29
- }
30
- ]
31
- }
32
- });
33
- export const isUnauthorized = (error)=>{
34
- return error instanceof HTTPError && error.response.statusCode === 401 && isGadgetRequest(error.request.options);
35
- };
36
- export const swallowUnauthorized = (error)=>{
37
- if (isUnauthorized(error)) {
38
- log.warn("swallowing unauthorized error", {
39
- error
40
- });
41
- return;
42
- }
43
- throw error;
44
- };
45
- export const loadCookie = ()=>{
46
- const token = readSession();
47
- return token && `session=${encodeURIComponent(token)};`;
48
- };
49
- const isGadgetRequest = (options)=>{
50
- return options.url instanceof URL && options.url.host === config.domains.services;
51
- };
52
-
53
- //# sourceMappingURL=http.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/http.ts"],"sourcesContent":["import { got, HTTPError, type OptionsInit } from \"got\";\nimport { config } from \"./config.js\";\nimport { createLogger } from \"./log.js\";\nimport { readSession, writeSession } from \"./session.js\";\n\nconst log = createLogger(\"http\");\n\nexport const http = got.extend({\n hooks: {\n beforeRequest: [\n (options) => {\n options.headers[\"user-agent\"] = config.versionFull;\n log.info(\"http request\", {\n method: options.method,\n url: options.url?.toString(),\n });\n },\n ],\n afterResponse: [\n (response) => {\n log.info(\"http response\", {\n method: response.request.options.method,\n url: response.request.options.url?.toString(),\n statusCode: response.statusCode,\n traceId: response.headers[\"x-trace-id\"],\n });\n\n if (response.statusCode === 401 && isGadgetRequest(response.request.options)) {\n writeSession(undefined);\n }\n return response;\n },\n ],\n },\n});\n\nexport const isUnauthorized = (error: unknown): boolean => {\n return error instanceof HTTPError && error.response.statusCode === 401 && isGadgetRequest(error.request.options);\n};\n\nexport const swallowUnauthorized = (error: unknown): void => {\n if (isUnauthorized(error)) {\n log.warn(\"swallowing unauthorized error\", { error });\n return;\n }\n throw error;\n};\n\nexport const loadCookie = (): string | undefined => {\n const token = readSession();\n return token && `session=${encodeURIComponent(token)};`;\n};\n\nconst isGadgetRequest = (options: OptionsInit) => {\n return options.url instanceof URL && options.url.host === config.domains.services;\n};\n"],"names":["got","HTTPError","config","createLogger","readSession","writeSession","log","http","extend","hooks","beforeRequest","options","headers","versionFull","info","method","url","toString","afterResponse","response","request","statusCode","traceId","isGadgetRequest","undefined","isUnauthorized","error","swallowUnauthorized","warn","loadCookie","token","encodeURIComponent","URL","host","domains","services"],"mappings":"AAAA,SAASA,GAAG,EAAEC,SAAS,QAA0B,MAAM;AACvD,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,YAAY,QAAQ,WAAW;AACxC,SAASC,WAAW,EAAEC,YAAY,QAAQ,eAAe;AAEzD,MAAMC,MAAMH,aAAa;AAEzB,OAAO,MAAMI,OAAOP,IAAIQ,MAAM,CAAC;IAC7BC,OAAO;QACLC,eAAe;YACb,CAACC;gBACCA,QAAQC,OAAO,CAAC,aAAa,GAAGV,OAAOW,WAAW;gBAClDP,IAAIQ,IAAI,CAAC,gBAAgB;oBACvBC,QAAQJ,QAAQI,MAAM;oBACtBC,KAAKL,QAAQK,GAAG,EAAEC;gBACpB;YACF;SACD;QACDC,eAAe;YACb,CAACC;gBACCb,IAAIQ,IAAI,CAAC,iBAAiB;oBACxBC,QAAQI,SAASC,OAAO,CAACT,OAAO,CAACI,MAAM;oBACvCC,KAAKG,SAASC,OAAO,CAACT,OAAO,CAACK,GAAG,EAAEC;oBACnCI,YAAYF,SAASE,UAAU;oBAC/BC,SAASH,SAASP,OAAO,CAAC,aAAa;gBACzC;gBAEA,IAAIO,SAASE,UAAU,KAAK,OAAOE,gBAAgBJ,SAASC,OAAO,CAACT,OAAO,GAAG;oBAC5EN,aAAamB;gBACf;gBACA,OAAOL;YACT;SACD;IACH;AACF,GAAG;AAEH,OAAO,MAAMM,iBAAiB,CAACC;IAC7B,OAAOA,iBAAiBzB,aAAayB,MAAMP,QAAQ,CAACE,UAAU,KAAK,OAAOE,gBAAgBG,MAAMN,OAAO,CAACT,OAAO;AACjH,EAAE;AAEF,OAAO,MAAMgB,sBAAsB,CAACD;IAClC,IAAID,eAAeC,QAAQ;QACzBpB,IAAIsB,IAAI,CAAC,iCAAiC;YAAEF;QAAM;QAClD;IACF;IACA,MAAMA;AACR,EAAE;AAEF,OAAO,MAAMG,aAAa;IACxB,MAAMC,QAAQ1B;IACd,OAAO0B,SAAS,CAAC,QAAQ,EAAEC,mBAAmBD,OAAO,CAAC,CAAC;AACzD,EAAE;AAEF,MAAMP,kBAAkB,CAACZ;IACvB,OAAOA,QAAQK,GAAG,YAAYgB,OAAOrB,QAAQK,GAAG,CAACiB,IAAI,KAAK/B,OAAOgC,OAAO,CAACC,QAAQ;AACnF"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/is.ts"],"sourcesContent":["import type { GraphQLError } from \"graphql\";\nimport type { SetOptional } from \"type-fest\";\nimport type { CloseEvent, ErrorEvent } from \"ws\";\nimport { z } from \"zod\";\n\nexport const isNil = (val: unknown): val is null | undefined => {\n return val === null || val === undefined;\n};\n\nexport const isString = (val: unknown): val is string => {\n return typeof val === \"string\";\n};\n\nexport const isObject = (val: unknown): val is object => {\n return val instanceof Object;\n};\n\n// eslint-disable-next-line @typescript-eslint/ban-types\nexport const isFunction = (val: unknown): val is Function => {\n return typeof val === \"function\";\n};\n\nexport const isError = (val: unknown): val is Error => {\n return val instanceof Error;\n};\n\nexport const isCloseEvent = (e: unknown): e is SetOptional<CloseEvent, \"target\"> => {\n return z.object({ type: z.string(), code: z.number(), reason: z.string(), wasClean: z.boolean() }).safeParse(e).success;\n};\n\nexport const isErrorEvent = (e: unknown): e is SetOptional<ErrorEvent, \"target\"> => {\n return z.object({ type: z.string(), message: z.string(), error: z.any() }).safeParse(e).success;\n};\n\nexport const isGraphQLErrors = (e: unknown): e is readonly GraphQLError[] => {\n return z.array(z.object({ message: z.string() })).safeParse(e).success;\n};\n"],"names":["z","isNil","val","undefined","isString","isObject","Object","isFunction","isError","Error","isCloseEvent","e","object","type","string","code","number","reason","wasClean","boolean","safeParse","success","isErrorEvent","message","error","any","isGraphQLErrors","array"],"mappings":"AAGA,SAASA,CAAC,QAAQ,MAAM;AAExB,OAAO,MAAMC,QAAQ,CAACC;IACpB,OAAOA,QAAQ,QAAQA,QAAQC;AACjC,EAAE;AAEF,OAAO,MAAMC,WAAW,CAACF;IACvB,OAAO,OAAOA,QAAQ;AACxB,EAAE;AAEF,OAAO,MAAMG,WAAW,CAACH;IACvB,OAAOA,eAAeI;AACxB,EAAE;AAEF,wDAAwD;AACxD,OAAO,MAAMC,aAAa,CAACL;IACzB,OAAO,OAAOA,QAAQ;AACxB,EAAE;AAEF,OAAO,MAAMM,UAAU,CAACN;IACtB,OAAOA,eAAeO;AACxB,EAAE;AAEF,OAAO,MAAMC,eAAe,CAACC;IAC3B,OAAOX,EAAEY,MAAM,CAAC;QAAEC,MAAMb,EAAEc,MAAM;QAAIC,MAAMf,EAAEgB,MAAM;QAAIC,QAAQjB,EAAEc,MAAM;QAAII,UAAUlB,EAAEmB,OAAO;IAAG,GAAGC,SAAS,CAACT,GAAGU,OAAO;AACzH,EAAE;AAEF,OAAO,MAAMC,eAAe,CAACX;IAC3B,OAAOX,EAAEY,MAAM,CAAC;QAAEC,MAAMb,EAAEc,MAAM;QAAIS,SAASvB,EAAEc,MAAM;QAAIU,OAAOxB,EAAEyB,GAAG;IAAG,GAAGL,SAAS,CAACT,GAAGU,OAAO;AACjG,EAAE;AAEF,OAAO,MAAMK,kBAAkB,CAACf;IAC9B,OAAOX,EAAE2B,KAAK,CAAC3B,EAAEY,MAAM,CAAC;QAAEW,SAASvB,EAAEc,MAAM;IAAG,IAAIM,SAAS,CAACT,GAAGU,OAAO;AACxE,EAAE"}
@@ -1,45 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-confusing-void-expression */ import { addBreadcrumb as addSentryBreadcrumb } from "@sentry/node";
2
- import Debug from "debug";
3
- import { serializeError } from "./errors.js";
4
- import { isFunction } from "./is.js";
5
- let longestName = 0;
6
- let longestMessage = 0;
7
- export const createLogger = (name, fields = {})=>{
8
- longestName = Math.max(longestName, name.length);
9
- const baseFields = isFunction(fields) ? fields : ()=>fields;
10
- const createLog = (level)=>{
11
- const debug = Debug(`ggt:${name}`);
12
- return (msg, fields)=>{
13
- longestMessage = Math.max(longestMessage, msg.length);
14
- fields = {
15
- ...baseFields(),
16
- ...fields
17
- };
18
- if ("error" in fields) {
19
- fields.error = serializeError(fields.error);
20
- }
21
- if (Object.keys(fields).length === 0) {
22
- debug("%s%s", " ".repeat(longestName - name.length), msg.padEnd(longestMessage));
23
- } else {
24
- debug("%s%s %o", " ".repeat(longestName - name.length), msg.padEnd(longestMessage), fields);
25
- }
26
- if (level === "debug") {
27
- // don't send debug logs to Sentry
28
- return;
29
- }
30
- addSentryBreadcrumb({
31
- level: level === "warn" ? "warning" : level,
32
- message: msg,
33
- data: fields
34
- });
35
- };
36
- };
37
- return {
38
- debug: createLog("debug"),
39
- info: createLog("info"),
40
- warn: createLog("warn"),
41
- error: createLog("error")
42
- };
43
- };
44
-
45
- //# sourceMappingURL=log.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/log.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-confusing-void-expression */\nimport { addBreadcrumb as addSentryBreadcrumb } from \"@sentry/node\";\nimport Debug from \"debug\";\nimport type { Jsonifiable } from \"type-fest\";\nimport { serializeError } from \"./errors.js\";\nimport { isFunction } from \"./is.js\";\n\ntype JsonifiableObject = { [Key in string]?: Jsonifiable } | { toJSON: () => Jsonifiable } | { error?: unknown };\n\nlet longestName = 0;\nlet longestMessage = 0;\n\nexport const createLogger = (name: string, fields: JsonifiableObject | (() => JsonifiableObject) = {}) => {\n longestName = Math.max(longestName, name.length);\n const baseFields = isFunction(fields) ? fields : () => fields;\n\n const createLog = (level: \"debug\" | \"info\" | \"warn\" | \"error\") => {\n const debug = Debug(`ggt:${name}`);\n\n return (msg: Lowercase<string>, fields?: JsonifiableObject) => {\n longestMessage = Math.max(longestMessage, msg.length);\n fields = { ...baseFields(), ...fields };\n if (\"error\" in fields) {\n fields.error = serializeError(fields.error);\n }\n\n if (Object.keys(fields).length === 0) {\n debug(\"%s%s\", \" \".repeat(longestName - name.length), msg.padEnd(longestMessage));\n } else {\n debug(\"%s%s %o\", \" \".repeat(longestName - name.length), msg.padEnd(longestMessage), fields);\n }\n\n if (level === \"debug\") {\n // don't send debug logs to Sentry\n return;\n }\n\n addSentryBreadcrumb({\n level: level === \"warn\" ? \"warning\" : level,\n message: msg,\n data: fields,\n });\n };\n };\n\n return {\n debug: createLog(\"debug\"),\n info: createLog(\"info\"),\n warn: createLog(\"warn\"),\n error: createLog(\"error\"),\n };\n};\n"],"names":["addBreadcrumb","addSentryBreadcrumb","Debug","serializeError","isFunction","longestName","longestMessage","createLogger","name","fields","Math","max","length","baseFields","createLog","level","debug","msg","error","Object","keys","repeat","padEnd","message","data","info","warn"],"mappings":"AAAA,kEAAkE,GAClE,SAASA,iBAAiBC,mBAAmB,QAAQ,eAAe;AACpE,OAAOC,WAAW,QAAQ;AAE1B,SAASC,cAAc,QAAQ,cAAc;AAC7C,SAASC,UAAU,QAAQ,UAAU;AAIrC,IAAIC,cAAc;AAClB,IAAIC,iBAAiB;AAErB,OAAO,MAAMC,eAAe,CAACC,MAAcC,SAAwD,CAAC,CAAC;IACnGJ,cAAcK,KAAKC,GAAG,CAACN,aAAaG,KAAKI,MAAM;IAC/C,MAAMC,aAAaT,WAAWK,UAAUA,SAAS,IAAMA;IAEvD,MAAMK,YAAY,CAACC;QACjB,MAAMC,QAAQd,MAAM,CAAC,IAAI,EAAEM,KAAK,CAAC;QAEjC,OAAO,CAACS,KAAwBR;YAC9BH,iBAAiBI,KAAKC,GAAG,CAACL,gBAAgBW,IAAIL,MAAM;YACpDH,SAAS;gBAAE,GAAGI,YAAY;gBAAE,GAAGJ,MAAM;YAAC;YACtC,IAAI,WAAWA,QAAQ;gBACrBA,OAAOS,KAAK,GAAGf,eAAeM,OAAOS,KAAK;YAC5C;YAEA,IAAIC,OAAOC,IAAI,CAACX,QAAQG,MAAM,KAAK,GAAG;gBACpCI,MAAM,QAAQ,IAAIK,MAAM,CAAChB,cAAcG,KAAKI,MAAM,GAAGK,IAAIK,MAAM,CAAChB;YAClE,OAAO;gBACLU,MAAM,WAAW,IAAIK,MAAM,CAAChB,cAAcG,KAAKI,MAAM,GAAGK,IAAIK,MAAM,CAAChB,iBAAiBG;YACtF;YAEA,IAAIM,UAAU,SAAS;gBACrB,kCAAkC;gBAClC;YACF;YAEAd,oBAAoB;gBAClBc,OAAOA,UAAU,SAAS,YAAYA;gBACtCQ,SAASN;gBACTO,MAAMf;YACR;QACF;IACF;IAEA,OAAO;QACLO,OAAOF,UAAU;QACjBW,MAAMX,UAAU;QAChBY,MAAMZ,UAAU;QAChBI,OAAOJ,UAAU;IACnB;AACF,EAAE"}
@@ -1,4 +0,0 @@
1
- // eslint-disable-next-line @typescript-eslint/no-empty-function
2
- export const noop = ()=>{};
3
-
4
- //# sourceMappingURL=noop.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/noop.ts"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/no-empty-function\nexport const noop = (): void => {};\n"],"names":["noop"],"mappings":"AAAA,gEAAgE;AAChE,OAAO,MAAMA,OAAO,KAAa,EAAE"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/services/notify.ts"],"sourcesContent":["import notifier, { type Notification } from \"node-notifier\";\nimport type WindowsBalloon from \"node-notifier/notifiers/balloon.js\";\nimport type Growl from \"node-notifier/notifiers/growl.js\";\nimport type NotificationCenter from \"node-notifier/notifiers/notificationcenter.js\";\nimport type NotifySend from \"node-notifier/notifiers/notifysend.js\";\nimport type WindowsToaster from \"node-notifier/notifiers/toaster.js\";\nimport path from \"node:path\";\nimport type { Jsonifiable } from \"type-fest\";\nimport { workspaceRoot } from \"./config.js\";\nimport { createLogger } from \"./log.js\";\n\nconst log = createLogger(\"notify\");\n\n/**\n * Sends a native OS notification to the user.\n *\n * @see {@link https://www.npmjs.com/package/node-notifier node-notifier}\n */\nexport const notify = (\n notification:\n | Notification\n | NotificationCenter.Notification\n | NotifySend.Notification\n | WindowsToaster.Notification\n | WindowsBalloon.Notification\n | Growl.Notification,\n) => {\n log.info(\"notifying user\", { notification: notification as Jsonifiable });\n\n notifier.notify(\n {\n title: \"Gadget\",\n contentImage: path.join(workspaceRoot, \"assets/favicon-128@4x.png\"),\n icon: path.join(workspaceRoot, \"assets/favicon-128@4x.png\"),\n sound: true,\n timeout: false,\n ...notification,\n },\n (error) => {\n if (error) {\n log.warn(\"error notifying user\", { notification: notification as Jsonifiable });\n }\n },\n );\n};\n"],"names":["notifier","path","workspaceRoot","createLogger","log","notify","notification","info","title","contentImage","join","icon","sound","timeout","error","warn"],"mappings":"AAAA,OAAOA,cAAqC,gBAAgB;AAM5D,OAAOC,UAAU,YAAY;AAE7B,SAASC,aAAa,QAAQ,cAAc;AAC5C,SAASC,YAAY,QAAQ,WAAW;AAExC,MAAMC,MAAMD,aAAa;AAEzB;;;;CAIC,GACD,OAAO,MAAME,SAAS,CACpBC;IAQAF,IAAIG,IAAI,CAAC,kBAAkB;QAAED,cAAcA;IAA4B;IAEvEN,SAASK,MAAM,CACb;QACEG,OAAO;QACPC,cAAcR,KAAKS,IAAI,CAACR,eAAe;QACvCS,MAAMV,KAAKS,IAAI,CAACR,eAAe;QAC/BU,OAAO;QACPC,SAAS;QACT,GAAGP,YAAY;IACjB,GACA,CAACQ;QACC,IAAIA,OAAO;YACTV,IAAIW,IAAI,CAAC,wBAAwB;gBAAET,cAAcA;YAA4B;QAC/E;IACF;AAEJ,EAAE"}