@gadgetinc/ggt 0.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +177 -0
  3. package/assets/favicon-128@4x.png +0 -0
  4. package/bin/dev +20 -0
  5. package/bin/dev.cmd +3 -0
  6. package/bin/run +7 -0
  7. package/bin/run.cmd +3 -0
  8. package/dist/commands/help.d.ts +16 -0
  9. package/dist/commands/help.js +37 -0
  10. package/dist/commands/help.js.map +1 -0
  11. package/dist/commands/login.d.ts +7 -0
  12. package/dist/commands/login.js +36 -0
  13. package/dist/commands/login.js.map +1 -0
  14. package/dist/commands/logout.d.ts +7 -0
  15. package/dist/commands/logout.js +41 -0
  16. package/dist/commands/logout.js.map +1 -0
  17. package/dist/commands/sync.d.ts +64 -0
  18. package/dist/commands/sync.js +534 -0
  19. package/dist/commands/sync.js.map +1 -0
  20. package/dist/commands/whoami.d.ts +7 -0
  21. package/dist/commands/whoami.js +45 -0
  22. package/dist/commands/whoami.js.map +1 -0
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +6 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/lib/base-command.d.ts +87 -0
  27. package/dist/lib/base-command.js +318 -0
  28. package/dist/lib/base-command.js.map +1 -0
  29. package/dist/lib/client.d.ts +39 -0
  30. package/dist/lib/client.js +155 -0
  31. package/dist/lib/client.js.map +1 -0
  32. package/dist/lib/env.d.ts +10 -0
  33. package/dist/lib/env.js +25 -0
  34. package/dist/lib/env.js.map +1 -0
  35. package/dist/lib/errors.d.ts +74 -0
  36. package/dist/lib/errors.js +320 -0
  37. package/dist/lib/errors.js.map +1 -0
  38. package/dist/lib/fs-utils.d.ts +18 -0
  39. package/dist/lib/fs-utils.js +108 -0
  40. package/dist/lib/fs-utils.js.map +1 -0
  41. package/dist/lib/help.d.ts +14 -0
  42. package/dist/lib/help.js +30 -0
  43. package/dist/lib/help.js.map +1 -0
  44. package/dist/lib/sleep.d.ts +5 -0
  45. package/dist/lib/sleep.js +23 -0
  46. package/dist/lib/sleep.js.map +1 -0
  47. package/dist/lib/types.d.ts +9 -0
  48. package/dist/lib/types.js +3 -0
  49. package/dist/lib/types.js.map +1 -0
  50. package/package.json +107 -0
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.run = void 0;
4
+ var core_1 = require("@oclif/core");
5
+ Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oCAAkC;AAAzB,2FAAA,GAAG,OAAA","sourcesContent":["export { run } from \"@oclif/core\";\n"]}
@@ -0,0 +1,87 @@
1
+ import type { Config } from "@oclif/core";
2
+ import { Command } from "@oclif/core";
3
+ import type { Got } from "got";
4
+ import type { Notification } from "node-notifier";
5
+ import type WindowsBalloon from "node-notifier/notifiers/balloon";
6
+ import type Growl from "node-notifier/notifiers/growl";
7
+ import type NotificationCenter from "node-notifier/notifiers/notificationcenter";
8
+ import type NotifySend from "node-notifier/notifiers/notifysend";
9
+ import type WindowsToaster from "node-notifier/notifiers/toaster";
10
+ import { Client } from "./client";
11
+ import type { App, User } from "./types";
12
+ export declare const ENDPOINT: string;
13
+ export declare abstract class BaseCommand extends Command {
14
+ /**
15
+ * Determines how high the command is listed in the README. The lower the number, the higher the command is listed. Equal numbers are
16
+ * sorted alphabetically.
17
+ */
18
+ static priority: number;
19
+ static globalFlags: {
20
+ app: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
21
+ debug: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
22
+ };
23
+ /**
24
+ * Indicates whether the command is being run with the `-D/--debug` flag.
25
+ */
26
+ debugEnabled: boolean;
27
+ /**
28
+ * Determines whether the command requires the user to be logged in or not.
29
+ * If true and the user is not logged in, the user will be prompted to login before the command is run.
30
+ */
31
+ readonly requireUser: boolean;
32
+ /**
33
+ * Determines whether the command requires a Gadget app to be selected or not.
34
+ * If true and an app hasn't been selected, the user will be prompted to select an app before the command is run.
35
+ *
36
+ * Implies {@linkcode requireUser requireUser = true}.
37
+ */
38
+ readonly requireApp: boolean;
39
+ /**
40
+ * The HTTP client to use for all HTTP requests.
41
+ *
42
+ * If a request is made to Gadget, the session token will be added to the request's headers.
43
+ */
44
+ readonly http: Got;
45
+ /**
46
+ * The GraphQL client to use for all Gadget API requests.
47
+ *
48
+ * Will be `undefined` if the user is not logged in or if the user has not selected an app.
49
+ *
50
+ * @see {@linkcode requireApp requireApp}
51
+ */
52
+ client: Client;
53
+ private _session?;
54
+ constructor(argv: string[], config: Config);
55
+ get session(): string | undefined;
56
+ set session(value: string | undefined);
57
+ init(): Promise<void>;
58
+ /**
59
+ * Sends a native OS notification to the user.
60
+ *
61
+ * @see {@link https://www.npmjs.com/package/node-notifier node-notifier}
62
+ */
63
+ notify(notification: Notification | NotificationCenter.Notification | NotifySend.Notification | WindowsToaster.Notification | WindowsBalloon.Notification | Growl.Notification): void;
64
+ /**
65
+ * Opens the Gadget login page in the user's default browser and waits for the user to login.
66
+ */
67
+ login(): Promise<void>;
68
+ /**
69
+ * @returns Whether the user was logged in or not.
70
+ */
71
+ logout(): boolean;
72
+ /**
73
+ * @returns The current user, or undefined if the user is not logged in.
74
+ */
75
+ getCurrentUser(): Promise<User | undefined>;
76
+ /**
77
+ * @returns A list of Gadget apps that the user has access to.
78
+ */
79
+ getApps(): Promise<App[]>;
80
+ /**
81
+ * Overrides the default `catch` behavior so we can control how errors are printed to the user. This is called automatically by oclif when
82
+ * an error is thrown during the `init` or `run` methods.
83
+ */
84
+ catch(cause: Error & {
85
+ exitCode?: number;
86
+ }): never;
87
+ }
@@ -0,0 +1,318 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseCommand = exports.ENDPOINT = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
6
+ const errors_1 = require("@oclif/core/lib/errors");
7
+ const errors_2 = require("@oclif/errors");
8
+ const debug_1 = tslib_1.__importDefault(require("debug"));
9
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
10
+ const get_port_1 = tslib_1.__importDefault(require("get-port"));
11
+ const got_1 = tslib_1.__importStar(require("got"));
12
+ const http_1 = require("http");
13
+ const inquirer_1 = require("inquirer");
14
+ const node_notifier_1 = require("node-notifier");
15
+ const open_1 = tslib_1.__importDefault(require("open"));
16
+ const path_1 = tslib_1.__importDefault(require("path"));
17
+ const client_1 = require("./client");
18
+ const env_1 = require("./env");
19
+ const errors_3 = require("./errors");
20
+ const fs_utils_1 = require("./fs-utils");
21
+ exports.ENDPOINT = env_1.Env.productionLike ? "https://app.gadget.dev" : "https://app.ggt.dev:3000";
22
+ class BaseCommand extends core_1.Command {
23
+ constructor(argv, config) {
24
+ super(argv, config);
25
+ /**
26
+ * Indicates whether the command is being run with the `-D/--debug` flag.
27
+ */
28
+ Object.defineProperty(this, "debugEnabled", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: false
33
+ });
34
+ /**
35
+ * Determines whether the command requires the user to be logged in or not.
36
+ * If true and the user is not logged in, the user will be prompted to login before the command is run.
37
+ */
38
+ Object.defineProperty(this, "requireUser", {
39
+ enumerable: true,
40
+ configurable: true,
41
+ writable: true,
42
+ value: false
43
+ });
44
+ /**
45
+ * Determines whether the command requires a Gadget app to be selected or not.
46
+ * If true and an app hasn't been selected, the user will be prompted to select an app before the command is run.
47
+ *
48
+ * Implies {@linkcode requireUser requireUser = true}.
49
+ */
50
+ Object.defineProperty(this, "requireApp", {
51
+ enumerable: true,
52
+ configurable: true,
53
+ writable: true,
54
+ value: false
55
+ });
56
+ /**
57
+ * The HTTP client to use for all HTTP requests.
58
+ *
59
+ * If a request is made to Gadget, the session token will be added to the request's headers.
60
+ */
61
+ Object.defineProperty(this, "http", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: void 0
66
+ });
67
+ /**
68
+ * The GraphQL client to use for all Gadget API requests.
69
+ *
70
+ * Will be `undefined` if the user is not logged in or if the user has not selected an app.
71
+ *
72
+ * @see {@linkcode requireApp requireApp}
73
+ */
74
+ Object.defineProperty(this, "client", {
75
+ enumerable: true,
76
+ configurable: true,
77
+ writable: true,
78
+ value: void 0
79
+ });
80
+ Object.defineProperty(this, "_session", {
81
+ enumerable: true,
82
+ configurable: true,
83
+ writable: true,
84
+ value: void 0
85
+ });
86
+ this.http = got_1.default.extend({
87
+ hooks: {
88
+ beforeRequest: [
89
+ (options) => {
90
+ options.headers["user-agent"] = this.config.userAgent;
91
+ if (options.url.origin === exports.ENDPOINT && this.session) {
92
+ options.headers = { ...options.headers, cookie: `session=${encodeURIComponent(this.session)};` };
93
+ }
94
+ },
95
+ ],
96
+ },
97
+ });
98
+ }
99
+ get session() {
100
+ try {
101
+ return (this._session ??= fs_extra_1.default.readFileSync(path_1.default.join(this.config.configDir, "session.txt"), "utf-8"));
102
+ }
103
+ catch (error) {
104
+ (0, fs_utils_1.ignoreEnoent)(error);
105
+ return undefined;
106
+ }
107
+ }
108
+ set session(value) {
109
+ this._session = value;
110
+ if (value) {
111
+ fs_extra_1.default.outputFileSync(path_1.default.join(this.config.configDir, "session.txt"), value);
112
+ }
113
+ else {
114
+ fs_extra_1.default.removeSync(path_1.default.join(this.config.configDir, "session.txt"));
115
+ }
116
+ }
117
+ async init() {
118
+ await super.init();
119
+ const { flags, argv } = await this.parse({ flags: BaseCommand.globalFlags, strict: false });
120
+ // remove global flags from argv
121
+ this.argv = argv;
122
+ if (flags.debug) {
123
+ this.debugEnabled = true;
124
+ debug_1.default.enable(`${this.config.bin}:*`);
125
+ }
126
+ if (!this.requireUser && !this.requireApp) {
127
+ return;
128
+ }
129
+ if (!(await this.getCurrentUser())) {
130
+ const { login } = await (0, inquirer_1.prompt)({
131
+ type: "confirm",
132
+ name: "login",
133
+ message: "You must be logged in to use this command. Would you like to log in?",
134
+ });
135
+ if (!login) {
136
+ return this.exit(0);
137
+ }
138
+ await this.login();
139
+ }
140
+ if (!this.requireApp) {
141
+ return;
142
+ }
143
+ let app = flags.app;
144
+ if (!app) {
145
+ ({ app } = await (0, inquirer_1.prompt)({
146
+ type: "list",
147
+ name: "app",
148
+ message: "Please select the app to use with this command.",
149
+ choices: await this.getApps().then((apps) => apps.map((app) => app.slug)),
150
+ }));
151
+ }
152
+ this.client = new client_1.Client(app, {
153
+ ws: {
154
+ headers: {
155
+ "user-agent": this.config.userAgent,
156
+ cookie: `session=${encodeURIComponent(this.session)};`,
157
+ },
158
+ },
159
+ });
160
+ }
161
+ /**
162
+ * Sends a native OS notification to the user.
163
+ *
164
+ * @see {@link https://www.npmjs.com/package/node-notifier node-notifier}
165
+ */
166
+ notify(notification) {
167
+ (0, node_notifier_1.notify)({
168
+ title: "Gadget",
169
+ contentImage: path_1.default.join(this.config.root, "assets", "favicon-128@4x.png"),
170
+ icon: path_1.default.join(this.config.root, "assets", "favicon-128@4x.png"),
171
+ sound: true,
172
+ timeout: false,
173
+ ...notification,
174
+ }, (error) => {
175
+ if (error)
176
+ this.warn(error);
177
+ });
178
+ }
179
+ /**
180
+ * Opens the Gadget login page in the user's default browser and waits for the user to login.
181
+ */
182
+ async login() {
183
+ let server;
184
+ try {
185
+ const port = await (0, get_port_1.default)();
186
+ const receiveSession = new Promise((resolve, reject) => {
187
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
188
+ server = (0, http_1.createServer)(async (req, res) => {
189
+ const redirectTo = new URL(`${exports.ENDPOINT}/auth/cli`);
190
+ try {
191
+ if (!req.url)
192
+ throw new Error("missing url");
193
+ const incomingUrl = new URL(req.url, `http://localhost:${port}`);
194
+ const session = incomingUrl.searchParams.get("session");
195
+ if (!session)
196
+ throw new Error("missing session");
197
+ this.session = session;
198
+ const user = await this.getCurrentUser();
199
+ if (!user)
200
+ throw new Error("missing current user");
201
+ if (user.name) {
202
+ this.log(`Hello, ${user.name} (${user.email})`);
203
+ }
204
+ else {
205
+ this.log(`Hello, ${user.email}`);
206
+ }
207
+ redirectTo.searchParams.set("success", "true");
208
+ resolve();
209
+ }
210
+ catch (error) {
211
+ this.session = undefined;
212
+ redirectTo.searchParams.set("success", "false");
213
+ reject(error);
214
+ }
215
+ finally {
216
+ res.writeHead(303, { Location: redirectTo.toString() });
217
+ res.end();
218
+ }
219
+ });
220
+ server.listen(port);
221
+ });
222
+ const url = new URL(`${exports.ENDPOINT}/auth/login`);
223
+ url.searchParams.set("returnTo", `${exports.ENDPOINT}/auth/cli/callback?port=${port}`);
224
+ await (0, open_1.default)(url.toString());
225
+ this.log("Your browser has been opened. Please log in to your account.");
226
+ await receiveSession;
227
+ }
228
+ finally {
229
+ server?.close();
230
+ }
231
+ }
232
+ /**
233
+ * @returns Whether the user was logged in or not.
234
+ */
235
+ logout() {
236
+ if (!this.session)
237
+ return false;
238
+ this.session = undefined;
239
+ return true;
240
+ }
241
+ /**
242
+ * @returns The current user, or undefined if the user is not logged in.
243
+ */
244
+ async getCurrentUser() {
245
+ if (!this.session)
246
+ return undefined;
247
+ try {
248
+ return await this.http(`${exports.ENDPOINT}/auth/api/current-user`).json();
249
+ }
250
+ catch (error) {
251
+ if (error instanceof got_1.HTTPError && error.response.statusCode === 401) {
252
+ this.logout();
253
+ return undefined;
254
+ }
255
+ throw error;
256
+ }
257
+ }
258
+ /**
259
+ * @returns A list of Gadget apps that the user has access to.
260
+ */
261
+ async getApps() {
262
+ if (!this.session)
263
+ return [];
264
+ return await this.http(`${exports.ENDPOINT}/auth/api/apps`).json();
265
+ }
266
+ /**
267
+ * Overrides the default `catch` behavior so we can control how errors are printed to the user. This is called automatically by oclif when
268
+ * an error is thrown during the `init` or `run` methods.
269
+ */
270
+ catch(cause) {
271
+ const error = cause instanceof errors_3.BaseError ? cause : new errors_3.UnexpectedError(cause);
272
+ console.error(error.render(this.config));
273
+ // The original implementation of `catch` re-throws the error so that it's caught and printed by oclif's `handle` method. We still want
274
+ // to end up in oclif's `handle` method, but we don't want it to print the error again so we throw an ExitError instead. This will cause
275
+ // `handle` to not print the error, but still exit with the correct exit code.
276
+ //
277
+ // catch: https://github.com/oclif/core/blob/12e31ff2288606e583e03bf774a3244f3136cd10/src/command.ts#L261
278
+ // handle: https://github.com/oclif/core/blob/12e31ff2288606e583e03bf774a3244f3136cd10/src/errors/handle.ts#L15
279
+ throw new errors_1.ExitError(process.exitCode ?? cause.exitCode ?? 1);
280
+ }
281
+ }
282
+ exports.BaseCommand = BaseCommand;
283
+ /**
284
+ * Determines how high the command is listed in the README. The lower the number, the higher the command is listed. Equal numbers are
285
+ * sorted alphabetically.
286
+ */
287
+ Object.defineProperty(BaseCommand, "priority", {
288
+ enumerable: true,
289
+ configurable: true,
290
+ writable: true,
291
+ value: Infinity
292
+ });
293
+ Object.defineProperty(BaseCommand, "globalFlags", {
294
+ enumerable: true,
295
+ configurable: true,
296
+ writable: true,
297
+ value: {
298
+ app: core_1.Flags.string({
299
+ char: "A",
300
+ summary: "The Gadget application this command applies to.",
301
+ helpGroup: "global",
302
+ helpValue: "name",
303
+ parse: (value) => {
304
+ const app = /^(https:\/\/)?(?<app>[\w-]+)(\..*)?/.exec(value)?.groups?.["app"];
305
+ if (!app)
306
+ throw new errors_2.CLIError("Flag '-A, --app=<name>' is invalid");
307
+ return Promise.resolve(app);
308
+ },
309
+ }),
310
+ debug: core_1.Flags.boolean({
311
+ char: "D",
312
+ summary: "Whether to output debug information.",
313
+ helpGroup: "global",
314
+ default: false,
315
+ }),
316
+ }
317
+ });
318
+ //# sourceMappingURL=base-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-command.js","sourceRoot":"","sources":["../../src/lib/base-command.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Config } from \"@oclif/core\";\nimport { Command, Flags } from \"@oclif/core\";\nimport { ExitError } from \"@oclif/core/lib/errors\";\nimport { CLIError } from \"@oclif/errors\";\nimport Debug from \"debug\";\nimport fs from \"fs-extra\";\nimport getPort from \"get-port\";\nimport type { Got } from \"got\";\nimport got, { HTTPError } from \"got\";\nimport type { Server } from \"http\";\nimport { createServer } from \"http\";\nimport { prompt } from \"inquirer\";\nimport type { Notification } from \"node-notifier\";\nimport { notify } from \"node-notifier\";\nimport type WindowsBalloon from \"node-notifier/notifiers/balloon\";\nimport type Growl from \"node-notifier/notifiers/growl\";\nimport type NotificationCenter from \"node-notifier/notifiers/notificationcenter\";\nimport type NotifySend from \"node-notifier/notifiers/notifysend\";\nimport type WindowsToaster from \"node-notifier/notifiers/toaster\";\nimport open from \"open\";\nimport path from \"path\";\nimport { Client } from \"./client\";\nimport { Env } from \"./env\";\nimport { BaseError, UnexpectedError as UnknownError } from \"./errors\";\nimport { ignoreEnoent } from \"./fs-utils\";\nimport type { App, User } from \"./types\";\n\nexport const ENDPOINT = Env.productionLike ? \"https://app.gadget.dev\" : \"https://app.ggt.dev:3000\";\n\nexport abstract class BaseCommand extends Command {\n /**\n * Determines how high the command is listed in the README. The lower the number, the higher the command is listed. Equal numbers are\n * sorted alphabetically.\n */\n static priority = Infinity;\n\n static override globalFlags = {\n app: Flags.string({\n char: \"A\",\n summary: \"The Gadget application this command applies to.\",\n helpGroup: \"global\",\n helpValue: \"name\",\n parse: (value) => {\n const app = /^(https:\\/\\/)?(?<app>[\\w-]+)(\\..*)?/.exec(value)?.groups?.[\"app\"];\n if (!app) throw new CLIError(\"Flag '-A, --app=<name>' is invalid\");\n return Promise.resolve(app);\n },\n }),\n debug: Flags.boolean({\n char: \"D\",\n summary: \"Whether to output debug information.\",\n helpGroup: \"global\",\n default: false,\n }),\n };\n\n /**\n * Indicates whether the command is being run with the `-D/--debug` flag.\n */\n debugEnabled = false;\n\n /**\n * Determines whether the command requires the user to be logged in or not.\n * If true and the user is not logged in, the user will be prompted to login before the command is run.\n */\n readonly requireUser: boolean = false;\n\n /**\n * Determines whether the command requires a Gadget app to be selected or not.\n * If true and an app hasn't been selected, the user will be prompted to select an app before the command is run.\n *\n * Implies {@linkcode requireUser requireUser = true}.\n */\n readonly requireApp: boolean = false;\n\n /**\n * The HTTP client to use for all HTTP requests.\n *\n * If a request is made to Gadget, the session token will be added to the request's headers.\n */\n readonly http: Got;\n\n /**\n * The GraphQL client to use for all Gadget API requests.\n *\n * Will be `undefined` if the user is not logged in or if the user has not selected an app.\n *\n * @see {@linkcode requireApp requireApp}\n */\n client!: Client;\n\n private _session?: string;\n\n constructor(argv: string[], config: Config) {\n super(argv, config);\n\n this.http = got.extend({\n hooks: {\n beforeRequest: [\n (options) => {\n options.headers[\"user-agent\"] = this.config.userAgent;\n if (options.url.origin === ENDPOINT && this.session) {\n options.headers = { ...options.headers, cookie: `session=${encodeURIComponent(this.session)};` };\n }\n },\n ],\n },\n });\n }\n\n get session(): string | undefined {\n try {\n return (this._session ??= fs.readFileSync(path.join(this.config.configDir, \"session.txt\"), \"utf-8\"));\n } catch (error) {\n ignoreEnoent(error);\n return undefined;\n }\n }\n\n set session(value: string | undefined) {\n this._session = value;\n if (value) {\n fs.outputFileSync(path.join(this.config.configDir, \"session.txt\"), value);\n } else {\n fs.removeSync(path.join(this.config.configDir, \"session.txt\"));\n }\n }\n\n override async init(): Promise<void> {\n await super.init();\n const { flags, argv } = await this.parse({ flags: BaseCommand.globalFlags, strict: false });\n\n // remove global flags from argv\n this.argv = argv;\n\n if (flags.debug) {\n this.debugEnabled = true;\n Debug.enable(`${this.config.bin}:*`);\n }\n\n if (!this.requireUser && !this.requireApp) {\n return;\n }\n\n if (!(await this.getCurrentUser())) {\n const { login } = await prompt<{ login: boolean }>({\n type: \"confirm\",\n name: \"login\",\n message: \"You must be logged in to use this command. Would you like to log in?\",\n });\n\n if (!login) {\n return this.exit(0);\n }\n\n await this.login();\n }\n\n if (!this.requireApp) {\n return;\n }\n\n let app = flags.app;\n if (!app) {\n ({ app } = await prompt<{ app: string }>({\n type: \"list\",\n name: \"app\",\n message: \"Please select the app to use with this command.\",\n choices: await this.getApps().then((apps) => apps.map((app) => app.slug)),\n }));\n }\n\n this.client = new Client(app, {\n ws: {\n headers: {\n \"user-agent\": this.config.userAgent,\n cookie: `session=${encodeURIComponent(this.session as string)};`,\n },\n },\n });\n }\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 */\n notify(\n notification:\n | Notification\n | NotificationCenter.Notification\n | NotifySend.Notification\n | WindowsToaster.Notification\n | WindowsBalloon.Notification\n | Growl.Notification\n ): void {\n notify(\n {\n title: \"Gadget\",\n contentImage: path.join(this.config.root, \"assets\", \"favicon-128@4x.png\"),\n icon: path.join(this.config.root, \"assets\", \"favicon-128@4x.png\"),\n sound: true,\n timeout: false,\n ...notification,\n },\n (error) => {\n if (error) this.warn(error);\n }\n );\n }\n\n /**\n * Opens the Gadget login page in the user's default browser and waits for the user to login.\n */\n async login(): Promise<void> {\n let server: Server | undefined;\n\n try {\n const port = await getPort();\n const receiveSession = new Promise<void>((resolve, reject) => {\n // eslint-disable-next-line @typescript-eslint/no-misused-promises\n server = createServer(async (req, res) => {\n const redirectTo = new URL(`${ENDPOINT}/auth/cli`);\n\n try {\n if (!req.url) throw new Error(\"missing url\");\n const incomingUrl = new URL(req.url, `http://localhost:${port}`);\n\n const session = incomingUrl.searchParams.get(\"session\");\n if (!session) throw new Error(\"missing session\");\n\n this.session = session;\n\n const user = await this.getCurrentUser();\n if (!user) throw new Error(\"missing current user\");\n\n if (user.name) {\n this.log(`Hello, ${user.name} (${user.email})`);\n } else {\n this.log(`Hello, ${user.email}`);\n }\n\n redirectTo.searchParams.set(\"success\", \"true\");\n resolve();\n } catch (error) {\n this.session = undefined;\n redirectTo.searchParams.set(\"success\", \"false\");\n reject(error);\n } finally {\n res.writeHead(303, { Location: redirectTo.toString() });\n res.end();\n }\n });\n\n server.listen(port);\n });\n\n const url = new URL(`${ENDPOINT}/auth/login`);\n url.searchParams.set(\"returnTo\", `${ENDPOINT}/auth/cli/callback?port=${port}`);\n await open(url.toString());\n\n this.log(\"Your browser has been opened. Please log in to your account.\");\n\n await receiveSession;\n } finally {\n server?.close();\n }\n }\n\n /**\n * @returns Whether the user was logged in or not.\n */\n logout(): boolean {\n if (!this.session) return false;\n\n this.session = undefined;\n return true;\n }\n\n /**\n * @returns The current user, or undefined if the user is not logged in.\n */\n async getCurrentUser(): Promise<User | undefined> {\n if (!this.session) return undefined;\n\n try {\n return await this.http(`${ENDPOINT}/auth/api/current-user`).json<User>();\n } catch (error) {\n if (error instanceof HTTPError && error.response.statusCode === 401) {\n this.logout();\n return undefined;\n }\n throw error;\n }\n }\n\n /**\n * @returns A list of Gadget apps that the user has access to.\n */\n async getApps(): Promise<App[]> {\n if (!this.session) return [];\n\n return await this.http(`${ENDPOINT}/auth/api/apps`).json<App[]>();\n }\n\n /**\n * Overrides the default `catch` behavior so we can control how errors are printed to the user. This is called automatically by oclif when\n * an error is thrown during the `init` or `run` methods.\n */\n override catch(cause: Error & { exitCode?: number }): never {\n const error = cause instanceof BaseError ? cause : new UnknownError(cause);\n console.error(error.render(this.config));\n\n // The original implementation of `catch` re-throws the error so that it's caught and printed by oclif's `handle` method. We still want\n // to end up in oclif's `handle` method, but we don't want it to print the error again so we throw an ExitError instead. This will cause\n // `handle` to not print the error, but still exit with the correct exit code.\n //\n // catch: https://github.com/oclif/core/blob/12e31ff2288606e583e03bf774a3244f3136cd10/src/command.ts#L261\n // handle: https://github.com/oclif/core/blob/12e31ff2288606e583e03bf774a3244f3136cd10/src/errors/handle.ts#L15\n throw new ExitError(process.exitCode ?? cause.exitCode ?? 1);\n }\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import type { ClientOptions, ExecutionResult } from "graphql-ws";
2
+ import type { JsonObject, SetOptional } from "type-fest";
3
+ import WebSocket from "ws";
4
+ import { ClientError } from "./errors";
5
+ declare enum ConnectionStatus {
6
+ CONNECTED = 0,
7
+ DISCONNECTED = 1,
8
+ RECONNECTING = 2
9
+ }
10
+ export declare class Client {
11
+ status: ConnectionStatus;
12
+ private _client;
13
+ constructor(app: string, options?: Partial<ClientOptions> & {
14
+ ws?: Partial<WebSocket.ClientOptions>;
15
+ });
16
+ subscribe<Data extends JsonObject, Variables extends JsonObject, Extensions extends JsonObject = JsonObject>(payload: Payload<Data, Variables>, sink: SetOptional<Sink<Data, Extensions>, "complete">): () => void;
17
+ subscribeUnwrap<Data extends JsonObject, Variables extends JsonObject>(payload: Payload<Data, Variables>, sink: {
18
+ next: (data: Data) => void;
19
+ error: (error: ClientError) => void;
20
+ }): () => void;
21
+ query<Data extends JsonObject, Variables extends JsonObject, Extensions extends JsonObject = JsonObject>(payload: Payload<Data, Variables>): Promise<ExecutionResult<Data, Extensions>>;
22
+ queryUnwrap<Data extends JsonObject, Variables extends JsonObject>(payload: Payload<Data, Variables>): Promise<Data>;
23
+ dispose(): Promise<void>;
24
+ }
25
+ export declare type Query<Data extends JsonObject, Variables extends JsonObject = JsonObject, Extensions extends JsonObject = JsonObject> = string & {
26
+ __TData?: Data;
27
+ __TVariables?: Variables;
28
+ __TExtensions?: Extensions;
29
+ };
30
+ export interface Payload<Data extends JsonObject, Variables extends JsonObject> {
31
+ readonly query: Query<Data, Variables>;
32
+ readonly variables?: Variables | (() => Variables) | null;
33
+ }
34
+ export interface Sink<Data extends JsonObject, Extensions extends JsonObject> {
35
+ next(value: ExecutionResult<Data, Extensions>): void;
36
+ error(error: ClientError): void;
37
+ complete(): void;
38
+ }
39
+ export {};
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Client = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const debug_1 = tslib_1.__importDefault(require("debug"));
6
+ const graphql_ws_1 = require("graphql-ws");
7
+ const lodash_1 = require("lodash");
8
+ const ws_1 = tslib_1.__importDefault(require("ws"));
9
+ const env_1 = require("./env");
10
+ const errors_1 = require("./errors");
11
+ const debug = (0, debug_1.default)("ggt:client");
12
+ var ConnectionStatus;
13
+ (function (ConnectionStatus) {
14
+ ConnectionStatus[ConnectionStatus["CONNECTED"] = 0] = "CONNECTED";
15
+ ConnectionStatus[ConnectionStatus["DISCONNECTED"] = 1] = "DISCONNECTED";
16
+ ConnectionStatus[ConnectionStatus["RECONNECTING"] = 2] = "RECONNECTING";
17
+ })(ConnectionStatus || (ConnectionStatus = {}));
18
+ class Client {
19
+ constructor(app, options) {
20
+ // assume the client is going to connect
21
+ Object.defineProperty(this, "status", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: ConnectionStatus.CONNECTED
26
+ });
27
+ Object.defineProperty(this, "_client", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: void 0
32
+ });
33
+ this._client = (0, graphql_ws_1.createClient)({
34
+ url: `wss://${app}.${env_1.Env.productionLike ? "gadget.app" : "ggt.pub:3000"}/edit/api/graphql-ws`,
35
+ shouldRetry: () => true,
36
+ webSocketImpl: class extends ws_1.default {
37
+ constructor(address, protocols, wsOptions) {
38
+ super(address, protocols, { ...wsOptions, ...options?.ws });
39
+ }
40
+ },
41
+ on: {
42
+ connecting: () => {
43
+ switch (this.status) {
44
+ case ConnectionStatus.DISCONNECTED:
45
+ this.status = ConnectionStatus.RECONNECTING;
46
+ debug("reconnecting...");
47
+ break;
48
+ case ConnectionStatus.RECONNECTING:
49
+ debug("retrying...");
50
+ break;
51
+ default:
52
+ debug("connecting...");
53
+ break;
54
+ }
55
+ },
56
+ connected: () => {
57
+ if (this.status === ConnectionStatus.RECONNECTING) {
58
+ debug("reconnected");
59
+ }
60
+ else {
61
+ debug("connected");
62
+ }
63
+ // let the other on connected listeners see what status we're in
64
+ setImmediate(() => (this.status = ConnectionStatus.CONNECTED));
65
+ },
66
+ closed: (e) => {
67
+ const event = e;
68
+ if (event.wasClean) {
69
+ debug("connection closed");
70
+ return;
71
+ }
72
+ if (this.status === ConnectionStatus.CONNECTED) {
73
+ this.status = ConnectionStatus.DISCONNECTED;
74
+ debug("disconnected");
75
+ }
76
+ },
77
+ error: (error) => {
78
+ if (this.status == ConnectionStatus.RECONNECTING) {
79
+ debug("failed to reconnect %o", { error });
80
+ }
81
+ else {
82
+ debug("connection error %o", { error });
83
+ }
84
+ },
85
+ ...options?.on,
86
+ },
87
+ ...options,
88
+ });
89
+ }
90
+ subscribe(payload, sink) {
91
+ let subscribePayload;
92
+ let removeConnectedListener;
93
+ if ((0, lodash_1.isFunction)(payload.variables)) {
94
+ // the caller wants us to re-evaluate the variables every time graphql-ws re-subscribes after reconnecting
95
+ subscribePayload = { ...payload, variables: payload.variables() };
96
+ removeConnectedListener = this._client.on("connected", () => {
97
+ if (this.status == ConnectionStatus.RECONNECTING) {
98
+ // subscribePayload.variables is supposed to be readonly (it's not) and payload.variables could been re-assigned (it won't)
99
+ subscribePayload.variables = payload.variables();
100
+ debug("re-sending query %s", subscribePayload.query);
101
+ }
102
+ });
103
+ }
104
+ else {
105
+ subscribePayload = payload;
106
+ }
107
+ debug("sending query %s", subscribePayload.query);
108
+ const unsubscribe = this._client.subscribe(subscribePayload, {
109
+ next: (result) => sink.next(result),
110
+ error: (error) => sink.error(new errors_1.ClientError(subscribePayload, error)),
111
+ complete: () => sink.complete?.() ?? lodash_1.noop,
112
+ });
113
+ return () => {
114
+ removeConnectedListener?.();
115
+ unsubscribe();
116
+ };
117
+ }
118
+ subscribeUnwrap(payload, sink) {
119
+ const unsubscribe = this.subscribe(payload, {
120
+ ...sink,
121
+ next: (result) => {
122
+ if (result.errors) {
123
+ unsubscribe();
124
+ sink.error(new errors_1.ClientError(payload, result.errors));
125
+ return;
126
+ }
127
+ if (!result.data) {
128
+ sink.error(new errors_1.ClientError(payload, "We received a response without data"));
129
+ unsubscribe();
130
+ return;
131
+ }
132
+ sink.next(result.data);
133
+ },
134
+ });
135
+ return unsubscribe;
136
+ }
137
+ query(payload) {
138
+ return new Promise((resolve, reject) => {
139
+ this.subscribe(payload, { next: resolve, error: reject });
140
+ });
141
+ }
142
+ async queryUnwrap(payload) {
143
+ const result = await this.query(payload);
144
+ if (result.errors)
145
+ throw new errors_1.ClientError(payload, result.errors);
146
+ if (!result.data)
147
+ throw new errors_1.ClientError(payload, "We received a response without data");
148
+ return result.data;
149
+ }
150
+ async dispose() {
151
+ await this._client.dispose();
152
+ }
153
+ }
154
+ exports.Client = Client;
155
+ //# sourceMappingURL=client.js.map