@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.
- package/README.md +1 -1
- package/lib/commands/list.js +11 -8
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/root.js +3 -3
- package/lib/commands/root.js.map +1 -1
- package/lib/commands/sync.js +38 -38
- package/lib/commands/sync.js.map +1 -1
- package/lib/services/app.js +1 -2
- package/lib/services/app.js.map +1 -1
- package/lib/services/args.js +3 -3
- package/lib/services/args.js.map +1 -1
- package/lib/services/collections.js +17 -0
- package/lib/services/collections.js.map +1 -0
- package/lib/services/config.js +5 -6
- package/lib/services/config.js.map +1 -1
- package/lib/services/debounce.js +21 -0
- package/lib/services/debounce.js.map +1 -0
- package/lib/services/defaults.js +8 -0
- package/lib/services/defaults.js.map +1 -0
- package/lib/services/edit-graphql.js +20 -11
- package/lib/services/edit-graphql.js.map +1 -1
- package/lib/services/errors.js +37 -34
- package/lib/services/errors.js.map +1 -1
- package/lib/services/filesync.js +33 -33
- package/lib/services/filesync.js.map +1 -1
- package/lib/services/{fs-utils.js → fs.js} +17 -15
- package/lib/services/fs.js.map +1 -0
- package/lib/services/is.js +39 -0
- package/lib/services/is.js.map +1 -0
- package/lib/services/log.js +5 -5
- package/lib/services/log.js.map +1 -1
- package/lib/services/noop.js +4 -0
- package/lib/services/noop.js.map +1 -0
- package/lib/services/output.js +21 -6
- package/lib/services/output.js.map +1 -1
- package/lib/services/promise.js +5 -3
- package/lib/services/promise.js.map +1 -1
- package/lib/services/prompt.js +22 -0
- package/lib/services/prompt.js.map +1 -0
- package/lib/services/session.js +6 -2
- package/lib/services/session.js.map +1 -1
- package/lib/services/sleep.js +8 -6
- package/lib/services/sleep.js.map +1 -1
- package/lib/services/user.js +4 -6
- package/lib/services/user.js.map +1 -1
- package/lib/services/version.js +2 -2
- package/lib/services/version.js.map +1 -1
- package/npm-shrinkwrap.json +503 -473
- package/package.json +9 -11
- package/lib/services/fs-utils.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/config.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport isWsl from \"is-wsl\";\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/services/config.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport isWsl from \"is-wsl\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\nimport normalizePackageData, { type Package } from \"normalize-package-data\";\n\n/**\n * The root directory of the ggt package.\n */\nexport const workspaceRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), \"../../\");\n\n/**\n * The package.json of the ggt package.\n */\nexport const pkgJson = (await fs.readJson(path.join(workspaceRoot, \"package.json\"))) as Package;\nnormalizePackageData(pkgJson, true);\n\n/**\n * Captures the name and nature of the environment\n */\nexport const env = {\n get value(): string {\n return process.env[\"GGT_ENV\"] || \"production\";\n },\n\n get productionLike(): boolean {\n return !this.developmentOrTestLike;\n },\n\n get developmentLike(): boolean {\n return this.value.startsWith(\"development\");\n },\n\n get testLike(): boolean {\n return this.value.startsWith(\"test\");\n },\n\n get developmentOrTestLike(): boolean {\n return this.developmentLike || this.testLike;\n },\n};\n\nexport const config = {\n get name(): string {\n return pkgJson.name;\n },\n\n get version(): string {\n return pkgJson.version;\n },\n\n /**\n * @example \"ggt/1.2.3 darwin-arm64 node-v16.0.0\"\n */\n get versionFull(): string {\n return `${this.name}/${this.version} ${this.platform}-${this.arch} node-${process.version}`;\n },\n\n get arch(): string {\n return os.arch() === \"ia32\" ? \"x86\" : os.arch();\n },\n\n get platform(): string {\n return isWsl ? \"wsl\" : os.platform();\n },\n\n get windows(): boolean {\n return process.platform === \"win32\";\n },\n\n get macos(): boolean {\n return process.platform === \"darwin\";\n },\n\n get shell(): string | undefined {\n const SHELL = process.env[\"SHELL\"] ?? os.userInfo().shell?.split(path.sep).pop();\n if (SHELL) {\n return SHELL.split(\"/\").at(-1);\n }\n if (this.windows && process.env[\"COMSPEC\"]) {\n return process.env[\"COMSPEC\"].split(/\\\\|\\//).at(-1);\n }\n return \"unknown\";\n },\n\n get homeDir(): string {\n if (process.env[\"HOME\"]) {\n return process.env[\"HOME\"];\n }\n\n if (this.windows) {\n if (process.env[\"HOMEDRIVE\"] && process.env[\"HOMEPATH\"]) {\n return path.join(process.env[\"HOMEDRIVE\"], process.env[\"HOMEPATH\"]);\n }\n if (process.env[\"USERPROFILE\"]) {\n return process.env[\"USERPROFILE\"];\n }\n }\n\n return os.homedir() || os.tmpdir();\n },\n\n /**\n * - Unix: `~/.config/ggt`\n * - Windows: `%LOCALAPPDATA%\\ggt`\n *\n * Can be overridden by `GGT_CONFIG_DIR`\n */\n get configDir(): string {\n if (process.env[\"GGT_CONFIG_DIR\"]) {\n return process.env[\"GGT_CONFIG_DIR\"];\n }\n\n const base = process.env[\"XDG_CONFIG_HOME\"] || (this.windows && process.env[\"LOCALAPPDATA\"]) || path.join(this.homeDir, \".config\");\n return path.join(base, \"ggt\");\n },\n\n /**\n * - Linux: `~/.cache/ggt`\n * - macOS: `~/Library/Caches/ggt`\n * - Windows: `%LOCALAPPDATA%\\ggt`\n *\n * Can be overridden with `GGT_CACHE_DIR`\n */\n get cacheDir(): string {\n if (process.env[\"GGT_CACHE_DIR\"]) {\n return process.env[\"GGT_CACHE_DIR\"];\n }\n\n if (this.macos) {\n return path.join(this.homeDir, \"Library/Caches/ggt\");\n }\n\n const base = process.env[\"XDG_CACHE_HOME\"] || (this.windows && process.env[\"LOCALAPPDATA\"]) || path.join(this.homeDir, \".cache\");\n return path.join(base, \"ggt\");\n },\n\n /**\n * - Unix: `~/.local/share/ggt`\n * - Windows: `%LOCALAPPDATA%\\ggt`\n *\n * Can be overridden with `GGT_DATA_DIR`\n */\n get dataDir(): string {\n if (process.env[\"GGT_DATA_DIR\"]) {\n return process.env[\"GGT_DATA_DIR\"];\n }\n\n const base = process.env[\"XDG_DATA_HOME\"] || (this.windows && process.env[\"LOCALAPPDATA\"]) || path.join(this.homeDir, \".local/share\");\n return path.join(base, \"ggt\");\n },\n\n /**\n * Domains for various Gadget services.\n */\n domains: {\n /**\n * The domain for the Gadget applications. This is where the user's application is hosted.\n */\n get app() {\n return process.env[\"GGT_GADGET_APP_DOMAIN\"] || (env.productionLike ? \"gadget.app\" : \"ggt.pub\");\n },\n\n /**\n * The domain for the Gadget services. This is where Gadget's API is hosted.\n */\n get services() {\n return process.env[\"GGT_GADGET_SERVICES_DOMAIN\"] || (env.productionLike ? \"app.gadget.dev\" : \"app.ggt.dev\");\n },\n },\n};\n"],"names":["fs","isWsl","os","path","process","fileURLToPath","normalizePackageData","workspaceRoot","join","dirname","url","pkgJson","readJson","env","value","productionLike","developmentOrTestLike","developmentLike","startsWith","testLike","config","name","version","versionFull","platform","arch","windows","macos","shell","SHELL","userInfo","split","sep","pop","at","homeDir","homedir","tmpdir","configDir","base","cacheDir","dataDir","domains","app","services"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const debounce = (delayMS, f)=>{
|
|
2
|
+
let timerId;
|
|
3
|
+
let upcomingCall;
|
|
4
|
+
const debounced = (...args)=>{
|
|
5
|
+
upcomingCall = ()=>{
|
|
6
|
+
upcomingCall = undefined;
|
|
7
|
+
timerId = undefined;
|
|
8
|
+
f(...args);
|
|
9
|
+
};
|
|
10
|
+
clearTimeout(timerId);
|
|
11
|
+
timerId = setTimeout(upcomingCall, delayMS);
|
|
12
|
+
};
|
|
13
|
+
debounced.flush = ()=>{
|
|
14
|
+
if (upcomingCall) {
|
|
15
|
+
upcomingCall();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return debounced;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=debounce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/debounce.ts"],"sourcesContent":["export type DebouncedFunc<F extends (...args: unknown[]) => void> = F & {\n flush: () => void;\n};\n\nexport const debounce = <F extends (...args: unknown[]) => void>(delayMS: number, f: F): DebouncedFunc<F> => {\n let timerId: NodeJS.Timeout | undefined;\n let upcomingCall: (() => void) | undefined;\n\n const debounced = ((...args) => {\n upcomingCall = () => {\n upcomingCall = undefined;\n timerId = undefined;\n f(...args);\n };\n\n clearTimeout(timerId);\n timerId = setTimeout(upcomingCall, delayMS);\n }) as DebouncedFunc<F>;\n\n debounced.flush = () => {\n if (upcomingCall) {\n upcomingCall();\n }\n };\n\n return debounced;\n};\n"],"names":["debounce","delayMS","f","timerId","upcomingCall","debounced","args","undefined","clearTimeout","setTimeout","flush"],"mappings":"AAIA,OAAO,MAAMA,WAAW,CAAyCC,SAAiBC;IAChF,IAAIC;IACJ,IAAIC;IAEJ,MAAMC,YAAa,CAAC,GAAGC;QACrBF,eAAe;YACbA,eAAeG;YACfJ,UAAUI;YACVL,KAAKI;QACP;QAEAE,aAAaL;QACbA,UAAUM,WAAWL,cAAcH;IACrC;IAEAI,UAAUK,KAAK,GAAG;QAChB,IAAIN,cAAc;YAChBA;QACF;IACF;IAEA,OAAOC;AACT,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/defaults.ts"],"sourcesContent":["export const defaults = <TObject extends object, TSource extends object>(\n object: TObject,\n source: TSource,\n): NonNullable<TSource & TObject> => {\n return { ...source, ...object };\n};\n"],"names":["defaults","object","source"],"mappings":"AAAA,OAAO,MAAMA,WAAW,CACtBC,QACAC;IAEA,OAAO;QAAE,GAAGA,MAAM;QAAE,GAAGD,MAAM;IAAC;AAChC,EAAE"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
2
2
|
import { createClient } from "graphql-ws";
|
|
3
|
-
import _ from "lodash";
|
|
4
3
|
import assert from "node:assert";
|
|
5
4
|
import WebSocket from "ws";
|
|
6
5
|
import { config } from "./config.js";
|
|
7
6
|
import { ClientError } from "./errors.js";
|
|
8
7
|
import { loadCookie } from "./http.js";
|
|
8
|
+
import { isFunction } from "./is.js";
|
|
9
9
|
import { createLogger } from "./log.js";
|
|
10
|
+
import { noop } from "./noop.js";
|
|
10
11
|
var ConnectionStatus;
|
|
11
12
|
(function(ConnectionStatus) {
|
|
12
13
|
ConnectionStatus[ConnectionStatus["CONNECTED"] = 0] = "CONNECTED";
|
|
@@ -52,8 +53,12 @@ const log = createLogger("edit-graphql");
|
|
|
52
53
|
* @returns The data returned by the server.
|
|
53
54
|
*/ async query(payload) {
|
|
54
55
|
const result = await this._query(payload);
|
|
55
|
-
if (result.errors)
|
|
56
|
-
|
|
56
|
+
if (result.errors) {
|
|
57
|
+
throw new ClientError(payload, result.errors);
|
|
58
|
+
}
|
|
59
|
+
if (!result.data) {
|
|
60
|
+
throw new ClientError(payload, "We received a response without data");
|
|
61
|
+
}
|
|
57
62
|
return result.data;
|
|
58
63
|
}
|
|
59
64
|
/**
|
|
@@ -67,8 +72,8 @@ const log = createLogger("edit-graphql");
|
|
|
67
72
|
* This method shouldn't be used directly. It's exposed for testing.
|
|
68
73
|
*/ _subscribe(payload, sink) {
|
|
69
74
|
let subscribePayload;
|
|
70
|
-
let removeConnectedListener =
|
|
71
|
-
if (
|
|
75
|
+
let removeConnectedListener = noop;
|
|
76
|
+
if (isFunction(payload.variables)) {
|
|
72
77
|
// the caller wants us to re-evaluate the variables every time
|
|
73
78
|
// graphql-ws re-subscribes after reconnecting
|
|
74
79
|
subscribePayload = {
|
|
@@ -76,9 +81,13 @@ const log = createLogger("edit-graphql");
|
|
|
76
81
|
variables: payload.variables()
|
|
77
82
|
};
|
|
78
83
|
removeConnectedListener = this._client.on("connected", ()=>{
|
|
79
|
-
if (this.status
|
|
80
|
-
|
|
81
|
-
|
|
84
|
+
if (this.status === 2) {
|
|
85
|
+
assert(isFunction(payload.variables));
|
|
86
|
+
subscribePayload = {
|
|
87
|
+
...payload,
|
|
88
|
+
variables: payload.variables()
|
|
89
|
+
};
|
|
90
|
+
const [type, operation] = subscribePayload.query.split(/ |\(/, 2);
|
|
82
91
|
log.info("re-sending graphql query", {
|
|
83
92
|
type,
|
|
84
93
|
operation
|
|
@@ -88,7 +97,7 @@ const log = createLogger("edit-graphql");
|
|
|
88
97
|
} else {
|
|
89
98
|
subscribePayload = payload;
|
|
90
99
|
}
|
|
91
|
-
const [type, operation] =
|
|
100
|
+
const [type, operation] = subscribePayload.query.split(/ |\(/, 2);
|
|
92
101
|
log.info("sending graphql query", {
|
|
93
102
|
type,
|
|
94
103
|
operation
|
|
@@ -123,7 +132,7 @@ const log = createLogger("edit-graphql");
|
|
|
123
132
|
_define_property(this, "_client", void 0);
|
|
124
133
|
this._client = createClient({
|
|
125
134
|
url: `wss://${app.slug}.${config.domains.app}/edit/api/graphql-ws`,
|
|
126
|
-
shouldRetry:
|
|
135
|
+
shouldRetry: ()=>true,
|
|
127
136
|
webSocketImpl: class extends WebSocket {
|
|
128
137
|
constructor(address, protocols, wsOptions){
|
|
129
138
|
// this cookie should be available since we were given an app which requires a cookie to load
|
|
@@ -175,7 +184,7 @@ const log = createLogger("edit-graphql");
|
|
|
175
184
|
}
|
|
176
185
|
},
|
|
177
186
|
error: (error)=>{
|
|
178
|
-
if (this.status
|
|
187
|
+
if (this.status === 2) {
|
|
179
188
|
log.error("failed to reconnect", {
|
|
180
189
|
error
|
|
181
190
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/edit-graphql.ts"],"sourcesContent":["import type { GraphQLError } from \"graphql\";\nimport type { ExecutionResult, SubscribePayload } from \"graphql-ws\";\nimport { createClient } from \"graphql-ws\";\nimport type { ClientRequestArgs } from \"http\";\nimport _ from \"lodash\";\nimport assert from \"node:assert\";\nimport type { JsonObject, SetOptional } from \"type-fest\";\nimport type { CloseEvent, ErrorEvent } from \"ws\";\nimport WebSocket from \"ws\";\nimport type { App } from \"./app.js\";\nimport { config } from \"./config.js\";\nimport { ClientError } from \"./errors.js\";\nimport { loadCookie } from \"./http.js\";\nimport { createLogger } from \"./log.js\";\n\nenum ConnectionStatus {\n CONNECTED,\n DISCONNECTED,\n RECONNECTING,\n}\n\nconst log = createLogger(\"edit-graphql\");\n\n/**\n * EditGraphQL is a GraphQL client connected to a Gadget application's /edit/api/graphql-ws endpoint.\n */\nexport class EditGraphQL {\n // assume the client is going to connect\n status = ConnectionStatus.CONNECTED;\n\n private _client: ReturnType<typeof createClient>;\n\n constructor(app: App) {\n this._client = createClient({\n url: `wss://${app.slug}.${config.domains.app}/edit/api/graphql-ws`,\n shouldRetry: _.constant(true),\n webSocketImpl: class extends WebSocket {\n constructor(address: string | URL, protocols?: string | string[], wsOptions?: WebSocket.ClientOptions | ClientRequestArgs) {\n // this cookie should be available since we were given an app which requires a cookie to load\n const cookie = loadCookie();\n assert(cookie, \"missing cookie when connecting to GraphQL API\");\n\n super(address, protocols, {\n ...wsOptions,\n headers: {\n ...wsOptions?.headers,\n \"user-agent\": config.versionFull,\n cookie,\n },\n });\n }\n },\n on: {\n connecting: () => {\n switch (this.status) {\n case ConnectionStatus.DISCONNECTED:\n this.status = ConnectionStatus.RECONNECTING;\n log.info(\"reconnecting\");\n break;\n case ConnectionStatus.RECONNECTING:\n log.info(\"retrying\");\n break;\n default:\n log.info(\"connecting\");\n break;\n }\n },\n connected: () => {\n if (this.status === ConnectionStatus.RECONNECTING) {\n log.info(\"reconnected\");\n } else {\n log.info(\"connected\");\n }\n\n // let the other on connected listeners see what status we're in\n setImmediate(() => (this.status = ConnectionStatus.CONNECTED));\n },\n closed: (e) => {\n const event = e as CloseEvent;\n if (event.wasClean) {\n log.info(\"connection closed\");\n return;\n }\n\n if (this.status === ConnectionStatus.CONNECTED) {\n this.status = ConnectionStatus.DISCONNECTED;\n log.info(\"disconnected\");\n }\n },\n error: (error) => {\n if (this.status == ConnectionStatus.RECONNECTING) {\n log.error(\"failed to reconnect\", { error });\n } else {\n log.error(\"connection error\", { error });\n }\n },\n },\n });\n }\n\n /**\n * Subscribe to a GraphQL query.\n *\n * This method is typically used to subscribe to a GraphQL\n * subscription. If you want to execute a GraphQL query or mutation,\n * use {@link EditGraphQL.query} instead.\n *\n * @param payload The query and variables to send to the server.\n * @param sink The callbacks to invoke when the server responds.\n * @returns A function to unsubscribe from the subscription.\n */\n subscribe<Data extends JsonObject, Variables extends JsonObject>(\n payload: Payload<Data, Variables>,\n sink: { next: (data: Data) => void; error: (error: ClientError) => void },\n ): () => void {\n const unsubscribe = this._subscribe(payload, {\n ...sink,\n next: (result) => {\n if (result.errors) {\n unsubscribe();\n sink.error(new ClientError(payload, result.errors));\n return;\n }\n\n if (!result.data) {\n sink.error(new ClientError(payload, \"Received a GraphQL response without errors or data\"));\n unsubscribe();\n return;\n }\n\n sink.next(result.data);\n },\n });\n\n return unsubscribe;\n }\n\n /**\n * Execute a GraphQL query or mutation.\n * @param payload The query and variables to send to the server.\n * @returns The data returned by the server.\n */\n async query<Data extends JsonObject, Variables extends JsonObject>(payload: Payload<Data, Variables>): Promise<Data> {\n const result = await this._query(payload);\n if (result.errors) throw new ClientError(payload, result.errors);\n if (!result.data) throw new ClientError(payload, \"We received a response without data\");\n return result.data;\n }\n\n /**\n * Close the connection to the server.\n */\n async dispose(): Promise<void> {\n await this._client.dispose();\n }\n\n /**\n * Internal method to subscribe to a GraphQL query.\n *\n * This method shouldn't be used directly. It's exposed for testing.\n */\n _subscribe<Data extends JsonObject, Variables extends JsonObject, Extensions extends JsonObject = JsonObject>(\n payload: Payload<Data, Variables>,\n sink: SetOptional<Sink<Data, Extensions>, \"complete\">,\n ): () => void {\n let subscribePayload: SubscribePayload;\n let removeConnectedListener = _.noop.bind(_);\n\n if (_.isFunction(payload.variables)) {\n // the caller wants us to re-evaluate the variables every time\n // graphql-ws re-subscribes after reconnecting\n subscribePayload = { ...payload, variables: payload.variables() };\n removeConnectedListener = this._client.on(\"connected\", () => {\n if (this.status == ConnectionStatus.RECONNECTING) {\n (subscribePayload as any).variables = (payload.variables as any)();\n const [type, operation] = _.split(subscribePayload.query, / |\\(/, 2);\n log.info(\"re-sending graphql query\", { type, operation });\n }\n });\n } else {\n subscribePayload = payload as SubscribePayload;\n }\n\n const [type, operation] = _.split(subscribePayload.query, / |\\(/, 2);\n log.info(\"sending graphql query\", { type, operation });\n\n const unsubscribe = this._client.subscribe(subscribePayload, {\n next: (result: ExecutionResult<Data, Extensions>) => {\n sink.next(result);\n },\n error: (error) => {\n sink.error(new ClientError(subscribePayload, error as Error | GraphQLError[] | CloseEvent | ErrorEvent));\n },\n complete: () => {\n sink.complete?.();\n },\n });\n\n return () => {\n removeConnectedListener();\n unsubscribe();\n };\n }\n\n private _query<Data extends JsonObject, Variables extends JsonObject, Extensions extends JsonObject = JsonObject>(\n payload: Payload<Data, Variables>,\n ): Promise<ExecutionResult<Data, Extensions>> {\n return new Promise((resolve, reject) => {\n this._subscribe<Data, Variables, Extensions>(payload, { next: resolve, error: reject });\n });\n }\n}\n\nexport type Query<\n Data extends JsonObject,\n Variables extends JsonObject = JsonObject,\n Extensions extends JsonObject = JsonObject,\n> = string & {\n __TData?: Data;\n __TVariables?: Variables;\n __TExtensions?: Extensions;\n};\n\nexport interface Payload<Data extends JsonObject, Variables extends JsonObject> {\n readonly query: Query<Data, Variables>;\n readonly variables?: Variables | (() => Variables) | null;\n}\n\nexport interface Sink<Data extends JsonObject, Extensions extends JsonObject> {\n next(value: ExecutionResult<Data, Extensions>): void;\n error(error: ClientError): void;\n complete(): void;\n}\n"],"names":["createClient","_","assert","WebSocket","config","ClientError","loadCookie","createLogger","ConnectionStatus","log","EditGraphQL","subscribe","payload","sink","unsubscribe","_subscribe","next","result","errors","error","data","query","_query","dispose","_client","subscribePayload","removeConnectedListener","noop","bind","isFunction","variables","on","status","type","operation","split","info","complete","Promise","resolve","reject","constructor","app","url","slug","domains","shouldRetry","constant","webSocketImpl","address","protocols","wsOptions","cookie","headers","versionFull","connecting","connected","setImmediate","closed","e","event","wasClean"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/services/edit-graphql.ts"],"sourcesContent":["import type { GraphQLError } from \"graphql\";\nimport type { ExecutionResult, SubscribePayload } from \"graphql-ws\";\nimport { createClient } from \"graphql-ws\";\nimport assert from \"node:assert\";\nimport type { ClientRequestArgs } from \"node:http\";\nimport type { JsonObject, SetOptional } from \"type-fest\";\nimport type { CloseEvent, ErrorEvent } from \"ws\";\nimport WebSocket from \"ws\";\nimport type { App } from \"./app.js\";\nimport { config } from \"./config.js\";\nimport { ClientError } from \"./errors.js\";\nimport { loadCookie } from \"./http.js\";\nimport { isFunction } from \"./is.js\";\nimport { createLogger } from \"./log.js\";\nimport { noop } from \"./noop.js\";\n\nenum ConnectionStatus {\n CONNECTED,\n DISCONNECTED,\n RECONNECTING,\n}\n\nconst log = createLogger(\"edit-graphql\");\n\n/**\n * EditGraphQL is a GraphQL client connected to a Gadget application's /edit/api/graphql-ws endpoint.\n */\nexport class EditGraphQL {\n // assume the client is going to connect\n status = ConnectionStatus.CONNECTED;\n\n private _client: ReturnType<typeof createClient>;\n\n constructor(app: App) {\n this._client = createClient({\n url: `wss://${app.slug}.${config.domains.app}/edit/api/graphql-ws`,\n shouldRetry: () => true,\n webSocketImpl: class extends WebSocket {\n constructor(address: string | URL, protocols?: string | string[], wsOptions?: WebSocket.ClientOptions | ClientRequestArgs) {\n // this cookie should be available since we were given an app which requires a cookie to load\n const cookie = loadCookie();\n assert(cookie, \"missing cookie when connecting to GraphQL API\");\n\n super(address, protocols, {\n ...wsOptions,\n headers: {\n ...wsOptions?.headers,\n \"user-agent\": config.versionFull,\n cookie,\n },\n });\n }\n },\n on: {\n connecting: () => {\n switch (this.status) {\n case ConnectionStatus.DISCONNECTED:\n this.status = ConnectionStatus.RECONNECTING;\n log.info(\"reconnecting\");\n break;\n case ConnectionStatus.RECONNECTING:\n log.info(\"retrying\");\n break;\n default:\n log.info(\"connecting\");\n break;\n }\n },\n connected: () => {\n if (this.status === ConnectionStatus.RECONNECTING) {\n log.info(\"reconnected\");\n } else {\n log.info(\"connected\");\n }\n\n // let the other on connected listeners see what status we're in\n setImmediate(() => (this.status = ConnectionStatus.CONNECTED));\n },\n closed: (e) => {\n const event = e as CloseEvent;\n if (event.wasClean) {\n log.info(\"connection closed\");\n return;\n }\n\n if (this.status === ConnectionStatus.CONNECTED) {\n this.status = ConnectionStatus.DISCONNECTED;\n log.info(\"disconnected\");\n }\n },\n error: (error) => {\n if (this.status === ConnectionStatus.RECONNECTING) {\n log.error(\"failed to reconnect\", { error });\n } else {\n log.error(\"connection error\", { error });\n }\n },\n },\n });\n }\n\n /**\n * Subscribe to a GraphQL query.\n *\n * This method is typically used to subscribe to a GraphQL\n * subscription. If you want to execute a GraphQL query or mutation,\n * use {@link EditGraphQL.query} instead.\n *\n * @param payload The query and variables to send to the server.\n * @param sink The callbacks to invoke when the server responds.\n * @returns A function to unsubscribe from the subscription.\n */\n subscribe<Data extends JsonObject, Variables extends JsonObject>(\n payload: Payload<Data, Variables>,\n sink: { next: (data: Data) => void; error: (error: ClientError) => void },\n ): () => void {\n const unsubscribe = this._subscribe(payload, {\n ...sink,\n next: (result) => {\n if (result.errors) {\n unsubscribe();\n sink.error(new ClientError(payload, result.errors));\n return;\n }\n\n if (!result.data) {\n sink.error(new ClientError(payload, \"Received a GraphQL response without errors or data\"));\n unsubscribe();\n return;\n }\n\n sink.next(result.data);\n },\n });\n\n return unsubscribe;\n }\n\n /**\n * Execute a GraphQL query or mutation.\n * @param payload The query and variables to send to the server.\n * @returns The data returned by the server.\n */\n async query<Data extends JsonObject, Variables extends JsonObject>(payload: Payload<Data, Variables>): Promise<Data> {\n const result = await this._query(payload);\n if (result.errors) {\n throw new ClientError(payload, result.errors);\n }\n if (!result.data) {\n throw new ClientError(payload, \"We received a response without data\");\n }\n return result.data;\n }\n\n /**\n * Close the connection to the server.\n */\n async dispose(): Promise<void> {\n await this._client.dispose();\n }\n\n /**\n * Internal method to subscribe to a GraphQL query.\n *\n * This method shouldn't be used directly. It's exposed for testing.\n */\n _subscribe<Data extends JsonObject, Variables extends JsonObject, Extensions extends JsonObject = JsonObject>(\n payload: Payload<Data, Variables>,\n sink: SetOptional<Sink<Data, Extensions>, \"complete\">,\n ): () => void {\n let subscribePayload: Payload<Data, Variables>;\n let removeConnectedListener = noop;\n\n if (isFunction(payload.variables)) {\n // the caller wants us to re-evaluate the variables every time\n // graphql-ws re-subscribes after reconnecting\n subscribePayload = { ...payload, variables: payload.variables() };\n removeConnectedListener = this._client.on(\"connected\", () => {\n if (this.status === ConnectionStatus.RECONNECTING) {\n assert(isFunction(payload.variables));\n subscribePayload = { ...payload, variables: payload.variables() };\n const [type, operation] = subscribePayload.query.split(/ |\\(/, 2);\n log.info(\"re-sending graphql query\", { type, operation });\n }\n });\n } else {\n subscribePayload = payload;\n }\n\n const [type, operation] = subscribePayload.query.split(/ |\\(/, 2);\n log.info(\"sending graphql query\", { type, operation });\n\n const unsubscribe = this._client.subscribe(subscribePayload as SubscribePayload, {\n next: (result: ExecutionResult<Data, Extensions>) => {\n sink.next(result);\n },\n error: (error) => {\n sink.error(new ClientError(subscribePayload, error as Error | GraphQLError[] | CloseEvent | ErrorEvent));\n },\n complete: () => {\n sink.complete?.();\n },\n });\n\n return () => {\n removeConnectedListener();\n unsubscribe();\n };\n }\n\n private _query<Data extends JsonObject, Variables extends JsonObject, Extensions extends JsonObject = JsonObject>(\n payload: Payload<Data, Variables>,\n ): Promise<ExecutionResult<Data, Extensions>> {\n return new Promise((resolve, reject) => {\n this._subscribe<Data, Variables, Extensions>(payload, { next: resolve, error: reject });\n });\n }\n}\n\nexport type Query<\n Data extends JsonObject,\n Variables extends JsonObject = JsonObject,\n Extensions extends JsonObject = JsonObject,\n> = string & {\n __TData?: Data;\n __TVariables?: Variables;\n __TExtensions?: Extensions;\n};\n\nexport interface Payload<Data extends JsonObject, Variables extends JsonObject> {\n readonly query: Query<Data, Variables>;\n readonly variables?: Variables | (() => Variables) | null;\n}\n\nexport interface Sink<Data extends JsonObject, Extensions extends JsonObject> {\n next(value: ExecutionResult<Data, Extensions>): void;\n error(error: ClientError): void;\n complete(): void;\n}\n"],"names":["createClient","assert","WebSocket","config","ClientError","loadCookie","isFunction","createLogger","noop","ConnectionStatus","log","EditGraphQL","subscribe","payload","sink","unsubscribe","_subscribe","next","result","errors","error","data","query","_query","dispose","_client","subscribePayload","removeConnectedListener","variables","on","status","type","operation","split","info","complete","Promise","resolve","reject","constructor","app","url","slug","domains","shouldRetry","webSocketImpl","address","protocols","wsOptions","cookie","headers","versionFull","connecting","connected","setImmediate","closed","e","event","wasClean"],"mappings":"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"}
|
package/lib/services/errors.js
CHANGED
|
@@ -2,18 +2,21 @@ import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
|
2
2
|
import * as Sentry from "@sentry/node";
|
|
3
3
|
import arg from "arg";
|
|
4
4
|
import cleanStack from "clean-stack";
|
|
5
|
-
import { randomUUID } from "crypto";
|
|
6
5
|
import { RequestError } from "got";
|
|
7
|
-
import
|
|
8
|
-
import os from "os";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import { inspect } from "node:util";
|
|
9
9
|
import { serializeError as baseSerializeError } from "serialize-error";
|
|
10
10
|
import { dedent } from "ts-dedent";
|
|
11
|
-
import {
|
|
11
|
+
import { compact, uniq } from "./collections.js";
|
|
12
12
|
import { config, env } from "./config.js";
|
|
13
|
+
import { isCloseEvent, isError, isErrorEvent, isGraphQLErrors } from "./is.js";
|
|
14
|
+
import { sprintln2 } from "./output.js";
|
|
13
15
|
let app;
|
|
14
16
|
let user;
|
|
15
17
|
export const setUser = (newUser)=>{
|
|
16
18
|
user = newUser;
|
|
19
|
+
// eslint-disable-next-line unicorn/no-null
|
|
17
20
|
Sentry.setUser(newUser ?? null);
|
|
18
21
|
};
|
|
19
22
|
export const setApp = (newApp)=>{
|
|
@@ -25,12 +28,18 @@ export const setApp = (newApp)=>{
|
|
|
25
28
|
/**
|
|
26
29
|
* Constructs a CLIError from a cause.
|
|
27
30
|
*/ static from(cause) {
|
|
28
|
-
if (cause instanceof CLIError)
|
|
29
|
-
|
|
31
|
+
if (cause instanceof CLIError) {
|
|
32
|
+
return cause;
|
|
33
|
+
}
|
|
34
|
+
if (cause instanceof arg.ArgError) {
|
|
35
|
+
return new ArgError(cause.message);
|
|
36
|
+
}
|
|
30
37
|
return new UnexpectedError(cause);
|
|
31
38
|
}
|
|
32
39
|
async capture() {
|
|
33
|
-
if (this.isBug
|
|
40
|
+
if (this.isBug === "no") {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
34
43
|
Sentry.getCurrentHub().captureException(this, {
|
|
35
44
|
event_id: this.sentryEventId,
|
|
36
45
|
captureContext: {
|
|
@@ -73,7 +82,7 @@ export const setApp = (newApp)=>{
|
|
|
73
82
|
* Turns this error into a user-friendly message that explains what went wrong and how to fix it. A good write up of
|
|
74
83
|
* what an error should look like can be found here: {@link https://clig.dev/#errors}
|
|
75
84
|
*/ render() {
|
|
76
|
-
return
|
|
85
|
+
return compact([
|
|
77
86
|
this.header(),
|
|
78
87
|
this.body(),
|
|
79
88
|
this.footer()
|
|
@@ -83,9 +92,11 @@ export const setApp = (newApp)=>{
|
|
|
83
92
|
return `${this.code}: ${this.message}`;
|
|
84
93
|
}
|
|
85
94
|
footer() {
|
|
86
|
-
if (this.isBug
|
|
95
|
+
if (this.isBug === "no") {
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
87
98
|
return dedent`
|
|
88
|
-
${this.isBug
|
|
99
|
+
${this.isBug === "yes" ? "This is a bug" : "If you think this is a bug"}, please submit an issue using the link below.
|
|
89
100
|
|
|
90
101
|
https://github.com/gadget-inc/ggt/issues/new?template=bug_report.yml&error-id=${this.sentryEventId}
|
|
91
102
|
`;
|
|
@@ -110,9 +121,11 @@ export const setApp = (newApp)=>{
|
|
|
110
121
|
}
|
|
111
122
|
/**
|
|
112
123
|
* Universal Error object to json blob serializer.
|
|
113
|
-
*
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
*
|
|
125
|
+
* Wraps `serialize-error` with some handy stuff, like special support
|
|
126
|
+
* for Got HTTP errors
|
|
127
|
+
*/ export const serializeError = (error)=>{
|
|
128
|
+
let serialized = baseSerializeError(Array.isArray(error) ? new AggregateError(error) : error);
|
|
116
129
|
if (typeof serialized == "string") {
|
|
117
130
|
serialized = {
|
|
118
131
|
message: serialized
|
|
@@ -127,7 +140,7 @@ export const setApp = (newApp)=>{
|
|
|
127
140
|
serialized["responseBody"] = inspect(error.response?.body);
|
|
128
141
|
}
|
|
129
142
|
return serialized;
|
|
130
|
-
}
|
|
143
|
+
};
|
|
131
144
|
export var IsBug;
|
|
132
145
|
(function(IsBug) {
|
|
133
146
|
IsBug["YES"] = "yes";
|
|
@@ -140,7 +153,7 @@ export var IsBug;
|
|
|
140
153
|
* Whenever possible, we should use a more specific error so that we can provide more useful information.
|
|
141
154
|
*/ export class UnexpectedError extends CLIError {
|
|
142
155
|
body() {
|
|
143
|
-
if (
|
|
156
|
+
if (isError(this.cause)) {
|
|
144
157
|
return cleanStack(this.cause.stack ?? this.stack);
|
|
145
158
|
}
|
|
146
159
|
return this.stack;
|
|
@@ -156,25 +169,22 @@ export var IsBug;
|
|
|
156
169
|
export class ClientError extends CLIError {
|
|
157
170
|
body() {
|
|
158
171
|
if (isGraphQLErrors(this.cause)) {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
let
|
|
162
|
-
|
|
163
|
-
output += `\n ${i + 1}. ${errors[i]?.message}`;
|
|
164
|
-
}
|
|
165
|
-
return output;
|
|
172
|
+
const errors = uniq(this.cause.map((x)=>x.message));
|
|
173
|
+
if (errors.length > 1) {
|
|
174
|
+
let n = 1;
|
|
175
|
+
return sprintln2("Gadget responded with multiple errors:").concat(` ${n++}. ${errors.join(`\n ${n++}. `)}`);
|
|
166
176
|
} else {
|
|
167
177
|
return dedent`
|
|
168
178
|
Gadget responded with the following error:
|
|
169
179
|
|
|
170
|
-
${
|
|
180
|
+
${errors[0]}
|
|
171
181
|
`;
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
if (isCloseEvent(this.cause)) {
|
|
175
185
|
return "The connection to Gadget closed unexpectedly.";
|
|
176
186
|
}
|
|
177
|
-
if (isErrorEvent(this.cause) ||
|
|
187
|
+
if (isErrorEvent(this.cause) || isError(this.cause)) {
|
|
178
188
|
return this.cause.message;
|
|
179
189
|
}
|
|
180
190
|
return this.cause;
|
|
@@ -187,7 +197,9 @@ export class ClientError extends CLIError {
|
|
|
187
197
|
this.payload = payload;
|
|
188
198
|
this.cause = cause;
|
|
189
199
|
this.isBug = "maybe";
|
|
190
|
-
// ErrorEvent and CloseEvent aren't serializable, so we reconstruct
|
|
200
|
+
// ErrorEvent and CloseEvent aren't serializable, so we reconstruct
|
|
201
|
+
// them into an object. We discard the `target` property because
|
|
202
|
+
// it's large and not that useful
|
|
191
203
|
if (isErrorEvent(cause)) {
|
|
192
204
|
this.cause = {
|
|
193
205
|
type: cause.type,
|
|
@@ -261,14 +273,5 @@ export class InvalidSyncFileError extends CLIError {
|
|
|
261
273
|
this.app ??= "<name of app>";
|
|
262
274
|
}
|
|
263
275
|
}
|
|
264
|
-
function isCloseEvent(e) {
|
|
265
|
-
return !_.isNil(e) && _.isString(e.type) && _.isNumber(e.code) && _.isString(e.reason) && _.isBoolean(e.wasClean);
|
|
266
|
-
}
|
|
267
|
-
function isErrorEvent(e) {
|
|
268
|
-
return !_.isNil(e) && _.isString(e.type) && _.isString(e.message) && !_.isNil(e.error);
|
|
269
|
-
}
|
|
270
|
-
function isGraphQLErrors(e) {
|
|
271
|
-
return _.isArray(e) && _.every(e, (e)=>!_.isNil(e) && _.isString(e.message) && _.isArray(e.locations ?? []) && _.isArray(e.path ?? []));
|
|
272
|
-
}
|
|
273
276
|
|
|
274
277
|
//# sourceMappingURL=errors.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/errors.ts"],"sourcesContent":["import * as Sentry from \"@sentry/node\";\nimport arg from \"arg\";\nimport cleanStack from \"clean-stack\";\nimport { randomUUID } from \"crypto\";\nimport { RequestError } from \"got\";\nimport type { GraphQLError } from \"graphql\";\nimport _ from \"lodash\";\nimport os from \"os\";\nimport { serializeError as baseSerializeError } from \"serialize-error\";\nimport { dedent } from \"ts-dedent\";\nimport type { SetOptional } from \"type-fest\";\nimport { inspect } from \"util\";\nimport type { CloseEvent, ErrorEvent } from \"ws\";\nimport type { App } from \"./app.js\";\nimport { config, env } from \"./config.js\";\nimport type { Payload } from \"./edit-graphql.js\";\nimport type { User } from \"./user.js\";\n\nlet app: App | undefined;\nlet user: User | undefined;\n\nexport const setUser = (newUser: User | undefined): void => {\n user = newUser;\n Sentry.setUser(newUser ?? null);\n};\n\nexport const setApp = (newApp: App | undefined): void => {\n app = newApp;\n};\n\n/**\n * Base class for all errors.\n */\nexport abstract class CLIError extends Error {\n /**\n * A GGT_CLI_SOMETHING human/machine readable unique identifier for this error.\n */\n code: string;\n\n /**\n * The Sentry event ID for this error.\n */\n sentryEventId = env.testLike ? \"00000000-0000-0000-0000-000000000000\" : randomUUID();\n\n /**\n * The underlying *thing* that caused this error.\n */\n cause?: any;\n\n /**\n * Assume the stack trace exists.\n */\n override stack!: string;\n\n /**\n * Indicates whether this error is considered a bug or not.\n */\n abstract isBug: IsBug;\n\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n Error.captureStackTrace(this, this.constructor);\n }\n\n /**\n * Constructs a CLIError from a cause.\n */\n static from(cause: unknown): CLIError {\n if (cause instanceof CLIError) return cause;\n if (cause instanceof arg.ArgError) return new ArgError(cause.message);\n return new UnexpectedError(cause);\n }\n\n async capture(): Promise<void> {\n if (this.isBug == IsBug.NO) return;\n\n Sentry.getCurrentHub().captureException(this, {\n event_id: this.sentryEventId,\n captureContext: {\n user: user ? { id: String(user.id), email: user.email, username: user.name ?? undefined } : undefined,\n tags: {\n applicationId: app ? app.id : undefined,\n arch: config.arch,\n isBug: this.isBug,\n code: this.code,\n environment: env.value,\n platform: config.platform,\n shell: config.shell,\n version: config.version,\n },\n contexts: {\n cause: this.cause ? serializeError(this.cause) : undefined,\n app: {\n command: `ggt ${process.argv.slice(2).join(\" \")}`,\n argv: process.argv,\n },\n device: {\n name: os.hostname(),\n family: os.type(),\n arch: os.arch(),\n },\n runtime: {\n name: process.release.name,\n version: process.version,\n },\n },\n },\n });\n\n await Sentry.flush(2000);\n }\n\n /**\n * Turns this error into a user-friendly message that explains what went wrong and how to fix it. A good write up of\n * what an error should look like can be found here: {@link https://clig.dev/#errors}\n */\n render(): string {\n return _.compact([this.header(), this.body(), this.footer()]).join(\"\\n\\n\");\n }\n\n protected header(): string {\n return `${this.code}: ${this.message}`;\n }\n\n protected footer(): string {\n if (this.isBug == IsBug.NO) return \"\";\n\n return dedent`\n ${this.isBug == IsBug.YES ? \"This is a bug\" : \"If you think this is a bug\"}, please submit an issue using the link below.\n\n https://github.com/gadget-inc/ggt/issues/new?template=bug_report.yml&error-id=${this.sentryEventId}\n `;\n }\n\n protected abstract body(): string;\n}\n\n/**\n * Universal Error object to json blob serializer.\n * Wraps `serialize-error` with some handy stuff, like special support for Got HTTP errors\n */\nexport function serializeError(error: unknown): Record<string, any> {\n let serialized = baseSerializeError(_.isArray(error) ? new AggregateError(error) : error);\n if (typeof serialized == \"string\") {\n serialized = { message: serialized };\n }\n\n if (error instanceof RequestError) {\n serialized[\"timings\"] = undefined;\n serialized[\"options\"] = {\n method: error.options.method,\n url: error.options.url instanceof URL ? error.options.url.toJSON() : error.options.url,\n };\n serialized[\"responseBody\"] = inspect(error.response?.body);\n }\n\n return serialized;\n}\n\nexport enum IsBug {\n YES = \"yes\",\n NO = \"no\",\n MAYBE = \"maybe\",\n}\n\n/**\n * Our \"catch all\" error. If this error is thrown, we almost certainly have a bug.\n *\n * Whenever possible, we should use a more specific error so that we can provide more useful information.\n */\nexport class UnexpectedError extends CLIError {\n isBug = IsBug.YES;\n\n constructor(override cause: unknown) {\n super(\"GGT_CLI_UNEXPECTED_ERROR\", \"An unexpected error occurred\");\n }\n\n protected body(): string {\n if (_.isError(this.cause)) {\n return cleanStack(this.cause.stack ?? this.stack);\n }\n return this.stack;\n }\n}\n\nexport class ClientError extends CLIError {\n isBug = IsBug.MAYBE;\n\n constructor(\n readonly payload: Payload<any, any>,\n override cause: string | Error | readonly GraphQLError[] | CloseEvent | ErrorEvent,\n ) {\n super(\"GGT_CLI_CLIENT_ERROR\", \"An error occurred while communicating with Gadget\");\n\n // ErrorEvent and CloseEvent aren't serializable, so we reconstruct them into an object. We discard the `target` property because it's large and not that useful\n if (isErrorEvent(cause)) {\n this.cause = {\n type: cause.type,\n message: cause.message,\n error: serializeError(cause.error),\n } as any;\n } else if (isCloseEvent(cause)) {\n this.cause = {\n type: cause.type,\n code: cause.code,\n reason: cause.reason,\n wasClean: cause.wasClean,\n } as any;\n }\n }\n\n override body(): string {\n if (isGraphQLErrors(this.cause)) {\n if (this.cause.length > 1) {\n const errors = _.uniqBy(this.cause, \"message\");\n\n let output = \"Gadget responded with multiple errors:\\n\";\n for (let i = 0; i < errors.length; i++) {\n output += `\\n ${i + 1}. ${errors[i]?.message}`;\n }\n\n return output;\n } else {\n return dedent`\n Gadget responded with the following error:\n\n ${this.cause[0]?.message}\n `;\n }\n }\n\n if (isCloseEvent(this.cause)) {\n return \"The connection to Gadget closed unexpectedly.\";\n }\n\n if (isErrorEvent(this.cause) || _.isError(this.cause)) {\n return this.cause.message;\n }\n\n return this.cause;\n }\n}\n\nexport class YarnNotFoundError extends CLIError {\n isBug = IsBug.NO;\n\n constructor() {\n super(\"GGT_CLI_YARN_NOT_FOUND\", \"Yarn not found\");\n }\n\n protected body(): string {\n return dedent`\n Yarn must be installed to sync your application. You can install it by running:\n\n $ npm install --global yarn\n\n For more information, see: https://classic.yarnpkg.com/en/docs/install\n `;\n }\n}\n\nexport class ArgError extends CLIError {\n isBug = IsBug.NO;\n\n constructor(message: string) {\n super(\"GGT_CLI_ARG_ERROR\", message);\n }\n\n // eslint-disable-next-line lodash/prefer-constant\n protected override header(): string {\n return \"\";\n }\n\n protected override body(): string {\n return this.message;\n }\n}\n\nexport class InvalidSyncFileError extends CLIError {\n isBug = IsBug.MAYBE;\n\n constructor(\n readonly dir: string,\n readonly app: string | undefined,\n ) {\n super(\"GGT_CLI_INVALID_SYNC_FILE\", \"The .gadget/sync.json file was invalid or not found\");\n this.app ??= \"<name of app>\";\n }\n\n protected body(): string {\n return dedent`\n We failed to read the Gadget metadata file in this directory:\n\n ${this.dir}\n\n If you're running \\`ggt sync\\` for the first time, we recommend using an empty directory such as:\n\n ~/gadget/${this.app}\n\n Otherwise, if you're sure you want to sync the contents of that directory to Gadget, run \\`ggt sync\\` again with the \\`--force\\` flag:\n\n $ ggt sync --app ${this.app} ${this.dir} --force\n\n You will be prompted to either merge your local files with your remote ones or reset your local files to your remote ones.\n `;\n }\n}\n\nfunction isCloseEvent(e: any): e is SetOptional<CloseEvent, \"target\"> {\n return !_.isNil(e) && _.isString(e.type) && _.isNumber(e.code) && _.isString(e.reason) && _.isBoolean(e.wasClean);\n}\n\nfunction isErrorEvent(e: any): e is SetOptional<ErrorEvent, \"target\"> {\n return !_.isNil(e) && _.isString(e.type) && _.isString(e.message) && !_.isNil(e.error);\n}\n\nfunction isGraphQLErrors(e: any): e is readonly GraphQLError[] {\n return _.isArray(e) && _.every(e, (e) => !_.isNil(e) && _.isString(e.message) && _.isArray(e.locations ?? []) && _.isArray(e.path ?? []));\n}\n"],"names":["Sentry","arg","cleanStack","randomUUID","RequestError","_","os","serializeError","baseSerializeError","dedent","inspect","config","env","app","user","setUser","newUser","setApp","newApp","CLIError","Error","from","cause","ArgError","message","UnexpectedError","capture","isBug","getCurrentHub","captureException","event_id","sentryEventId","captureContext","id","String","email","username","name","undefined","tags","applicationId","arch","code","environment","value","platform","shell","version","contexts","command","process","argv","slice","join","device","hostname","family","type","runtime","release","flush","render","compact","header","body","footer","constructor","testLike","stack","captureStackTrace","error","serialized","isArray","AggregateError","method","options","url","URL","toJSON","response","IsBug","isError","ClientError","isGraphQLErrors","length","errors","uniqBy","output","i","isCloseEvent","isErrorEvent","payload","reason","wasClean","YarnNotFoundError","InvalidSyncFileError","dir","e","isNil","isString","isNumber","isBoolean","every","locations","path"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/services/errors.ts"],"sourcesContent":["import * as Sentry from \"@sentry/node\";\nimport arg from \"arg\";\nimport cleanStack from \"clean-stack\";\nimport { RequestError } from \"got\";\nimport type { GraphQLError } from \"graphql\";\nimport { randomUUID } from \"node:crypto\";\nimport os from \"node:os\";\nimport { inspect } from \"node:util\";\nimport { serializeError as baseSerializeError, type ErrorObject } from \"serialize-error\";\nimport { dedent } from \"ts-dedent\";\nimport type { JsonObject } from \"type-fest\";\nimport type { CloseEvent, ErrorEvent } from \"ws\";\nimport type { App } from \"./app.js\";\nimport { compact, uniq } from \"./collections.js\";\nimport { config, env } from \"./config.js\";\nimport type { Payload } from \"./edit-graphql.js\";\nimport { isCloseEvent, isError, isErrorEvent, isGraphQLErrors } from \"./is.js\";\nimport { sprintln2 } from \"./output.js\";\nimport type { User } from \"./user.js\";\n\nlet app: App | undefined;\nlet user: User | undefined;\n\nexport const setUser = (newUser: User | undefined): void => {\n user = newUser;\n // eslint-disable-next-line unicorn/no-null\n Sentry.setUser(newUser ?? null);\n};\n\nexport const setApp = (newApp: App | undefined): void => {\n app = newApp;\n};\n\n/**\n * Base class for all errors.\n */\nexport abstract class CLIError extends Error {\n /**\n * A GGT_CLI_SOMETHING human/machine readable unique identifier for this error.\n */\n code: string;\n\n /**\n * The Sentry event ID for this error.\n */\n sentryEventId = env.testLike ? \"00000000-0000-0000-0000-000000000000\" : randomUUID();\n\n /**\n * The underlying *thing* that caused this error.\n */\n cause?: unknown;\n\n /**\n * Assume the stack trace exists.\n */\n override stack!: string;\n\n /**\n * Indicates whether this error is considered a bug or not.\n */\n abstract isBug: IsBug;\n\n constructor(code: string, message: string) {\n super(message);\n this.code = code;\n Error.captureStackTrace(this, this.constructor);\n }\n\n /**\n * Constructs a CLIError from a cause.\n */\n static from(cause: unknown): CLIError {\n if (cause instanceof CLIError) {\n return cause;\n }\n if (cause instanceof arg.ArgError) {\n return new ArgError(cause.message);\n }\n return new UnexpectedError(cause);\n }\n\n async capture(): Promise<void> {\n if (this.isBug === IsBug.NO) {\n return;\n }\n\n Sentry.getCurrentHub().captureException(this, {\n event_id: this.sentryEventId,\n captureContext: {\n user: user ? { id: String(user.id), email: user.email, username: user.name ?? undefined } : undefined,\n tags: {\n applicationId: app ? app.id : undefined,\n arch: config.arch,\n isBug: this.isBug,\n code: this.code,\n environment: env.value,\n platform: config.platform,\n shell: config.shell,\n version: config.version,\n },\n contexts: {\n cause: this.cause ? serializeError(this.cause) : undefined,\n app: {\n command: `ggt ${process.argv.slice(2).join(\" \")}`,\n argv: process.argv,\n },\n device: {\n name: os.hostname(),\n family: os.type(),\n arch: os.arch(),\n },\n runtime: {\n name: process.release.name,\n version: process.version,\n },\n },\n },\n });\n\n await Sentry.flush(2000);\n }\n\n /**\n * Turns this error into a user-friendly message that explains what went wrong and how to fix it. A good write up of\n * what an error should look like can be found here: {@link https://clig.dev/#errors}\n */\n render(): string {\n return compact([this.header(), this.body(), this.footer()]).join(\"\\n\\n\");\n }\n\n protected header(): string {\n return `${this.code}: ${this.message}`;\n }\n\n protected footer(): string {\n if (this.isBug === IsBug.NO) {\n return \"\";\n }\n\n return dedent`\n ${this.isBug === IsBug.YES ? \"This is a bug\" : \"If you think this is a bug\"}, please submit an issue using the link below.\n\n https://github.com/gadget-inc/ggt/issues/new?template=bug_report.yml&error-id=${this.sentryEventId}\n `;\n }\n\n protected abstract body(): string;\n}\n\n/**\n * Universal Error object to json blob serializer.\n *\n * Wraps `serialize-error` with some handy stuff, like special support\n * for Got HTTP errors\n */\nexport const serializeError = (error: unknown): ErrorObject => {\n let serialized = baseSerializeError(Array.isArray(error) ? new AggregateError(error) : error);\n if (typeof serialized == \"string\") {\n serialized = { message: serialized };\n }\n\n if (error instanceof RequestError) {\n serialized[\"timings\"] = undefined;\n serialized[\"options\"] = {\n method: error.options.method,\n url: error.options.url instanceof URL ? error.options.url.toJSON() : error.options.url,\n };\n serialized[\"responseBody\"] = inspect(error.response?.body);\n }\n\n return serialized;\n};\n\nexport enum IsBug {\n YES = \"yes\",\n NO = \"no\",\n MAYBE = \"maybe\",\n}\n\n/**\n * Our \"catch all\" error. If this error is thrown, we almost certainly have a bug.\n *\n * Whenever possible, we should use a more specific error so that we can provide more useful information.\n */\nexport class UnexpectedError extends CLIError {\n isBug = IsBug.YES;\n\n constructor(override cause: unknown) {\n super(\"GGT_CLI_UNEXPECTED_ERROR\", \"An unexpected error occurred\");\n }\n\n protected body(): string {\n if (isError(this.cause)) {\n return cleanStack(this.cause.stack ?? this.stack);\n }\n return this.stack;\n }\n}\n\nexport class ClientError extends CLIError {\n isBug = IsBug.MAYBE;\n\n constructor(\n readonly payload: Payload<JsonObject, JsonObject>,\n override cause: string | Error | readonly GraphQLError[] | CloseEvent | ErrorEvent,\n ) {\n super(\"GGT_CLI_CLIENT_ERROR\", \"An error occurred while communicating with Gadget\");\n\n // ErrorEvent and CloseEvent aren't serializable, so we reconstruct\n // them into an object. We discard the `target` property because\n // it's large and not that useful\n if (isErrorEvent(cause)) {\n this.cause = {\n type: cause.type,\n message: cause.message,\n error: serializeError(cause.error),\n } as ErrorEvent;\n } else if (isCloseEvent(cause)) {\n this.cause = {\n type: cause.type,\n code: cause.code,\n reason: cause.reason,\n wasClean: cause.wasClean,\n } as CloseEvent;\n }\n }\n\n override body(): string {\n if (isGraphQLErrors(this.cause)) {\n const errors = uniq(this.cause.map((x) => x.message));\n if (errors.length > 1) {\n let n = 1;\n return sprintln2(\"Gadget responded with multiple errors:\").concat(` ${n++}. ${errors.join(`\\n ${n++}. `)}`);\n } else {\n return dedent`\n Gadget responded with the following error:\n\n ${errors[0]}\n `;\n }\n }\n\n if (isCloseEvent(this.cause)) {\n return \"The connection to Gadget closed unexpectedly.\";\n }\n\n if (isErrorEvent(this.cause) || isError(this.cause)) {\n return this.cause.message;\n }\n\n return this.cause;\n }\n}\n\nexport class YarnNotFoundError extends CLIError {\n isBug = IsBug.NO;\n\n constructor() {\n super(\"GGT_CLI_YARN_NOT_FOUND\", \"Yarn not found\");\n }\n\n protected body(): string {\n return dedent`\n Yarn must be installed to sync your application. You can install it by running:\n\n $ npm install --global yarn\n\n For more information, see: https://classic.yarnpkg.com/en/docs/install\n `;\n }\n}\n\nexport class ArgError extends CLIError {\n isBug = IsBug.NO;\n\n constructor(message: string) {\n super(\"GGT_CLI_ARG_ERROR\", message);\n }\n\n // eslint-disable-next-line lodash/prefer-constant\n protected override header(): string {\n return \"\";\n }\n\n protected override body(): string {\n return this.message;\n }\n}\n\nexport class InvalidSyncFileError extends CLIError {\n isBug = IsBug.MAYBE;\n\n constructor(\n readonly dir: string,\n readonly app: string | undefined,\n ) {\n super(\"GGT_CLI_INVALID_SYNC_FILE\", \"The .gadget/sync.json file was invalid or not found\");\n this.app ??= \"<name of app>\";\n }\n\n protected body(): string {\n return dedent`\n We failed to read the Gadget metadata file in this directory:\n\n ${this.dir}\n\n If you're running \\`ggt sync\\` for the first time, we recommend using an empty directory such as:\n\n ~/gadget/${this.app}\n\n Otherwise, if you're sure you want to sync the contents of that directory to Gadget, run \\`ggt sync\\` again with the \\`--force\\` flag:\n\n $ ggt sync --app ${this.app} ${this.dir} --force\n\n You will be prompted to either merge your local files with your remote ones or reset your local files to your remote ones.\n `;\n }\n}\n"],"names":["Sentry","arg","cleanStack","RequestError","randomUUID","os","inspect","serializeError","baseSerializeError","dedent","compact","uniq","config","env","isCloseEvent","isError","isErrorEvent","isGraphQLErrors","sprintln2","app","user","setUser","newUser","setApp","newApp","CLIError","Error","from","cause","ArgError","message","UnexpectedError","capture","isBug","getCurrentHub","captureException","event_id","sentryEventId","captureContext","id","String","email","username","name","undefined","tags","applicationId","arch","code","environment","value","platform","shell","version","contexts","command","process","argv","slice","join","device","hostname","family","type","runtime","release","flush","render","header","body","footer","constructor","testLike","stack","captureStackTrace","error","serialized","Array","isArray","AggregateError","method","options","url","URL","toJSON","response","IsBug","ClientError","errors","map","x","length","n","concat","payload","reason","wasClean","YarnNotFoundError","InvalidSyncFileError","dir"],"mappings":"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"}
|