@gadgetinc/ggt 0.2.0 → 0.2.2
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 +6 -6
- package/bin/dev.js +2 -2
- package/bin/run.js +1 -1
- package/lib/__generated__/graphql.js.map +1 -1
- package/lib/commands/help.js +3 -3
- package/lib/commands/help.js.map +1 -1
- package/lib/commands/list.js +4 -4
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/login.js +2 -2
- package/lib/commands/login.js.map +1 -1
- package/lib/commands/logout.js +2 -2
- package/lib/commands/logout.js.map +1 -1
- package/lib/commands/sync.js +239 -83
- package/lib/commands/sync.js.map +1 -1
- package/lib/commands/whoami.js +2 -2
- package/lib/commands/whoami.js.map +1 -1
- package/lib/{utils → services}/base-command.js +25 -10
- package/lib/services/base-command.js.map +1 -0
- package/lib/{utils → services}/client.js +70 -21
- package/lib/services/client.js.map +1 -0
- package/lib/{utils → services}/context.js +42 -27
- package/lib/services/context.js.map +1 -0
- package/lib/{utils → services}/errors.js +8 -8
- package/lib/services/errors.js.map +1 -0
- package/lib/{utils → services}/flags.js +4 -3
- package/lib/services/flags.js.map +1 -0
- package/lib/{utils → services}/fs-utils.js +6 -2
- package/lib/services/fs-utils.js.map +1 -0
- package/lib/{utils → services}/help.js +1 -1
- package/lib/services/help.js.map +1 -0
- package/lib/services/promise.js.map +1 -0
- package/lib/{utils → services}/sleep.js +6 -2
- package/lib/services/sleep.js.map +1 -0
- package/npm-shrinkwrap.json +2378 -2704
- package/oclif.manifest.json +35 -15
- package/package.json +35 -34
- package/lib/utils/base-command.js.map +0 -1
- package/lib/utils/client.js.map +0 -1
- package/lib/utils/context.js.map +0 -1
- package/lib/utils/errors.js.map +0 -1
- package/lib/utils/flags.js.map +0 -1
- package/lib/utils/fs-utils.js.map +0 -1
- package/lib/utils/help.js.map +0 -1
- package/lib/utils/promise.js.map +0 -1
- package/lib/utils/sleep.js.map +0 -1
- /package/lib/{utils → services}/promise.js +0 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
2
|
+
import { addBreadcrumb, setUser } from "@sentry/node";
|
|
2
3
|
import assert from "assert";
|
|
4
|
+
import Debug from "debug";
|
|
3
5
|
import fs from "fs-extra";
|
|
4
6
|
import { HTTPError, got } from "got";
|
|
5
7
|
import _ from "lodash";
|
|
6
8
|
import path from "path";
|
|
7
9
|
import { ignoreEnoent } from "./fs-utils.js";
|
|
10
|
+
const debug = Debug("ggt:context");
|
|
8
11
|
export class Context {
|
|
9
12
|
get session() {
|
|
10
13
|
if (this._session) return this._session;
|
|
@@ -32,6 +35,7 @@ export class Context {
|
|
|
32
35
|
if (this._user) return this._user;
|
|
33
36
|
try {
|
|
34
37
|
this._user = await this._request(`https://${this.domains.services}/auth/api/current-user`).json();
|
|
38
|
+
setUser(this._user);
|
|
35
39
|
return this._user;
|
|
36
40
|
} catch (error) {
|
|
37
41
|
if (error instanceof HTTPError && error.response.statusCode === 401) {
|
|
@@ -51,7 +55,7 @@ export class Context {
|
|
|
51
55
|
}
|
|
52
56
|
async setApp(appOrSlug) {
|
|
53
57
|
if (_.isString(appOrSlug)) {
|
|
54
|
-
const app = await this.getAvailableApps().then((apps)=>
|
|
58
|
+
const app = await this.getAvailableApps().then((apps)=>_.find(apps, (app)=>app.slug == appOrSlug));
|
|
55
59
|
assert(app, `attempted to set app to "${appOrSlug}" but no app with that name or slug was found`);
|
|
56
60
|
this.app = app;
|
|
57
61
|
} else {
|
|
@@ -63,6 +67,11 @@ export class Context {
|
|
|
63
67
|
this._user = undefined;
|
|
64
68
|
this.app = undefined;
|
|
65
69
|
this._availableApps = [];
|
|
70
|
+
setUser(null);
|
|
71
|
+
}
|
|
72
|
+
addBreadcrumb(breadcrumb) {
|
|
73
|
+
addBreadcrumb(breadcrumb);
|
|
74
|
+
debug("breadcrumb %O", breadcrumb);
|
|
66
75
|
}
|
|
67
76
|
constructor(){
|
|
68
77
|
/**
|
|
@@ -72,9 +81,38 @@ export class Context {
|
|
|
72
81
|
* able to access it from anywhere. To do this, we created this global variable that references the Config. It is set
|
|
73
82
|
* by the init function in the BaseCommand.
|
|
74
83
|
*/ _define_property(this, "config", void 0);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Captures the name and nature of the environment
|
|
86
|
+
*/ _define_property(this, "env", {
|
|
87
|
+
get value () {
|
|
88
|
+
return process.env["GGT_ENV"] ?? "production";
|
|
89
|
+
},
|
|
90
|
+
get productionLike () {
|
|
91
|
+
return _.startsWith(this.value, "production");
|
|
92
|
+
},
|
|
93
|
+
get developmentLike () {
|
|
94
|
+
return _.startsWith(this.value, "development");
|
|
95
|
+
},
|
|
96
|
+
get testLike () {
|
|
97
|
+
return _.startsWith(this.value, "test");
|
|
98
|
+
},
|
|
99
|
+
get developmentOrTestLike () {
|
|
100
|
+
return this.developmentLike || this.testLike;
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
/**
|
|
104
|
+
* Domains for various Gadget services.
|
|
105
|
+
*/ _define_property(this, "domains", {
|
|
106
|
+
/**
|
|
107
|
+
* The domain for the Gadget applications. This is where the user applications are hosted.
|
|
108
|
+
*/ app: process.env["GGT_GADGET_APP_DOMAIN"] ?? (this.env.productionLike ? "gadget.app" : "ggt.pub"),
|
|
109
|
+
/**
|
|
110
|
+
* The domain for the Gadget services. This is where the Gadget's API is hosted.
|
|
111
|
+
*/ services: process.env["GGT_GADGET_SERVICES_DOMAIN"] ?? (this.env.productionLike ? "app.gadget.dev" : "app.ggt.dev")
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* The current Gadget application the CLI is operating on.
|
|
115
|
+
*/ _define_property(this, "app", void 0);
|
|
78
116
|
_define_property(this, "_session", void 0);
|
|
79
117
|
_define_property(this, "_user", void 0);
|
|
80
118
|
_define_property(this, "_availableApps", []);
|
|
@@ -90,29 +128,6 @@ export class Context {
|
|
|
90
128
|
]
|
|
91
129
|
}
|
|
92
130
|
}));
|
|
93
|
-
this.domains = {
|
|
94
|
-
app: process.env["GGT_GADGET_APP_DOMAIN"] || (this.env.productionLike ? "gadget.app" : "ggt.pub"),
|
|
95
|
-
services: process.env["GGT_GADGET_SERVICES_DOMAIN"] || (this.env.productionLike ? "app.gadget.dev" : "app.ggt.dev")
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Captures the name and nature of the environment
|
|
101
|
-
*/ class Env {
|
|
102
|
-
get value() {
|
|
103
|
-
return process.env["GGT_ENV"] || "production";
|
|
104
|
-
}
|
|
105
|
-
get productionLike() {
|
|
106
|
-
return this.value.startsWith("production");
|
|
107
|
-
}
|
|
108
|
-
get developmentLike() {
|
|
109
|
-
return this.value.startsWith("development");
|
|
110
|
-
}
|
|
111
|
-
get testLike() {
|
|
112
|
-
return this.value.startsWith("test");
|
|
113
|
-
}
|
|
114
|
-
get developmentOrTestLike() {
|
|
115
|
-
return this.developmentLike || this.testLike;
|
|
116
131
|
}
|
|
117
132
|
}
|
|
118
133
|
export const context = new Context();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/context.ts"],"sourcesContent":["import type { Config } from \"@oclif/core\";\nimport { addBreadcrumb, setUser, type Breadcrumb as SentryBreadcrumb } from \"@sentry/node\";\nimport assert from \"assert\";\nimport Debug from \"debug\";\nimport fs from \"fs-extra\";\nimport { HTTPError, got } from \"got\";\nimport _ from \"lodash\";\nimport path from \"path\";\nimport { ignoreEnoent } from \"./fs-utils.js\";\n\nconst debug = Debug(\"ggt:context\");\n\nexport class Context {\n /**\n * A reference to oclif's {@linkcode Config}.\n *\n * By default, oclif's {@linkcode Config} is only available as an instance property on a Command, but we want to be\n * able to access it from anywhere. To do this, we created this global variable that references the Config. It is set\n * by the init function in the BaseCommand.\n */\n config!: Config;\n\n /**\n * Captures the name and nature of the environment\n */\n env = {\n get value(): string {\n return process.env[\"GGT_ENV\"] ?? \"production\";\n },\n get productionLike(): boolean {\n return _.startsWith(this.value, \"production\");\n },\n get developmentLike(): boolean {\n return _.startsWith(this.value, \"development\");\n },\n get testLike(): boolean {\n return _.startsWith(this.value, \"test\");\n },\n get developmentOrTestLike(): boolean {\n return this.developmentLike || this.testLike;\n },\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 applications are hosted.\n */\n app: process.env[\"GGT_GADGET_APP_DOMAIN\"] ?? (this.env.productionLike ? \"gadget.app\" : \"ggt.pub\"),\n\n /**\n * The domain for the Gadget services. This is where the Gadget's API is hosted.\n */\n services: process.env[\"GGT_GADGET_SERVICES_DOMAIN\"] ?? (this.env.productionLike ? \"app.gadget.dev\" : \"app.ggt.dev\"),\n };\n\n /**\n * The current Gadget application the CLI is operating on.\n */\n app?: App;\n\n private _session?: string;\n\n private _user?: User;\n\n private _availableApps: App[] = [];\n\n private _request = got.extend({\n hooks: {\n beforeRequest: [\n (options) => {\n options.headers[\"user-agent\"] = this.config.userAgent;\n if (options.url instanceof URL && options.url.host === this.domains.services && this.session) {\n options.headers[\"cookie\"] = `session=${encodeURIComponent(this.session)};`;\n }\n },\n ],\n },\n });\n\n get session(): string | undefined {\n if (this._session) return this._session;\n\n try {\n this._session = fs.readFileSync(path.join(this.config.configDir, \"session.txt\"), \"utf-8\");\n return this._session;\n } catch (error) {\n ignoreEnoent(error);\n return undefined;\n }\n }\n\n set session(value: string | undefined) {\n this.clear();\n this._session = value;\n if (this._session) {\n fs.outputFileSync(path.join(this.config.configDir, \"session.txt\"), this._session);\n } else {\n fs.removeSync(path.join(this.config.configDir, \"session.txt\"));\n }\n }\n\n /**\n * @returns The current user, or undefined if the user is not logged in.\n */\n async getUser(): Promise<User | undefined> {\n if (!this.session) return undefined;\n if (this._user) return this._user;\n\n try {\n this._user = await this._request(`https://${this.domains.services}/auth/api/current-user`).json<User>();\n setUser(this._user);\n return this._user;\n } catch (error) {\n if (error instanceof HTTPError && error.response.statusCode === 401) {\n this.session = undefined;\n return undefined;\n }\n throw error;\n }\n }\n\n /**\n * @returns The list of Gadget applications the current user has access to.\n */\n async getAvailableApps(): Promise<App[]> {\n if (!this.session) return [];\n if (this._availableApps.length > 0) return this._availableApps;\n\n this._availableApps = await this._request(`https://${this.domains.services}/auth/api/apps`).json<App[]>();\n return this._availableApps;\n }\n\n async setApp(appOrSlug?: App | string): Promise<void> {\n if (_.isString(appOrSlug)) {\n const app = await this.getAvailableApps().then((apps) => _.find(apps, (app) => app.slug == appOrSlug));\n assert(app, `attempted to set app to \"${appOrSlug}\" but no app with that name or slug was found`);\n this.app = app;\n } else {\n this.app = appOrSlug;\n }\n }\n\n clear(): void {\n this._session = undefined;\n this._user = undefined;\n this.app = undefined;\n this._availableApps = [];\n setUser(null);\n }\n\n addBreadcrumb(breadcrumb: Breadcrumb) {\n addBreadcrumb(breadcrumb);\n debug(\"breadcrumb %O\", breadcrumb);\n }\n}\n\nexport const context = new Context();\n\nexport interface Breadcrumb extends SentryBreadcrumb {\n category: \"command\" | \"client\" | \"sync\";\n message: Capitalize<string>;\n}\n\nexport interface User {\n id: string | number;\n email: string;\n name?: string;\n}\n\nexport interface App {\n id: string | number;\n slug: string;\n primaryDomain: string;\n hasSplitEnvironments: boolean;\n}\n"],"names":["addBreadcrumb","setUser","assert","Debug","fs","HTTPError","got","_","path","ignoreEnoent","debug","Context","session","_session","readFileSync","join","config","configDir","error","undefined","value","clear","outputFileSync","removeSync","getUser","_user","_request","domains","services","json","response","statusCode","getAvailableApps","_availableApps","length","setApp","appOrSlug","isString","app","then","apps","find","slug","breadcrumb","env","process","productionLike","startsWith","developmentLike","testLike","developmentOrTestLike","extend","hooks","beforeRequest","options","headers","userAgent","url","URL","host","encodeURIComponent","context"],"mappings":"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"}
|
|
@@ -2,16 +2,16 @@ import { _ as _class_private_field_get } from "@swc/helpers/_/_class_private_fie
|
|
|
2
2
|
import { _ as _class_private_field_init } from "@swc/helpers/_/_class_private_field_init";
|
|
3
3
|
import { _ as _class_private_field_set } from "@swc/helpers/_/_class_private_field_set";
|
|
4
4
|
import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
5
|
+
import * as Sentry from "@sentry/node";
|
|
5
6
|
import cleanStack from "clean-stack";
|
|
6
|
-
import {
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
8
|
+
import { RequestError } from "got";
|
|
7
9
|
import _ from "lodash";
|
|
10
|
+
import os from "os";
|
|
8
11
|
import { serializeError as baseSerializeError } from "serialize-error";
|
|
9
12
|
import { dedent } from "ts-dedent";
|
|
10
13
|
import { inspect } from "util";
|
|
11
14
|
import { context } from "./context.js";
|
|
12
|
-
import os from "os";
|
|
13
|
-
import * as Sentry from "@sentry/node";
|
|
14
|
-
import { randomUUID } from "crypto";
|
|
15
15
|
/**
|
|
16
16
|
* Base class for all errors.
|
|
17
17
|
*
|
|
@@ -114,8 +114,8 @@ import { randomUUID } from "crypto";
|
|
|
114
114
|
message: serialized
|
|
115
115
|
};
|
|
116
116
|
}
|
|
117
|
-
if (error instanceof
|
|
118
|
-
|
|
117
|
+
if (error instanceof RequestError) {
|
|
118
|
+
serialized["timings"] = undefined;
|
|
119
119
|
serialized["options"] = {
|
|
120
120
|
method: error.options.method,
|
|
121
121
|
url: error.options.url instanceof URL ? error.options.url.toJSON() : error.options.url
|
|
@@ -245,7 +245,7 @@ export class InvalidSyncFileError extends BaseError {
|
|
|
245
245
|
|
|
246
246
|
If you're running \`ggt sync\` for the first time, we recommend using an empty directory such as:
|
|
247
247
|
|
|
248
|
-
~/gadget/${this.app
|
|
248
|
+
~/gadget/${this.app ?? "<name of app>"}
|
|
249
249
|
|
|
250
250
|
Otherwise, if you're sure you want to sync the contents of that directory to Gadget, run \`ggt sync\` again with the \`--force\` flag:
|
|
251
251
|
|
|
@@ -293,7 +293,7 @@ function isErrorEvent(e) {
|
|
|
293
293
|
return !_.isNil(e) && _.isString(e.type) && _.isString(e.message) && !_.isNil(e.error);
|
|
294
294
|
}
|
|
295
295
|
function isGraphQLErrors(e) {
|
|
296
|
-
return _.isArray(e) &&
|
|
296
|
+
return _.isArray(e) && _.every(e, (e)=>!_.isNil(e) && _.isString(e.message) && _.isArray(e.locations ?? []) && _.isArray(e.path ?? []));
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/errors.ts"],"sourcesContent":["import * as Sentry from \"@sentry/node\";\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 Sync from \"../commands/sync.js\";\nimport type { Payload } from \"./client.js\";\nimport { context } from \"./context.js\";\n\n/**\n * Base class for all errors.\n *\n * Inspired by gadget's GadgetError and oclif's PrettyPrintableError.\n */\nexport abstract class BaseError 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 = context.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 async capture(): Promise<void> {\n if (this.isBug == IsBug.NO) return;\n\n const user = await context.getUser().catch(_.noop.bind(_));\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,\n tags: {\n applicationId: context.app?.id,\n arch: context.config.arch,\n isBug: this.isBug,\n code: this.code,\n environment: context.env.value,\n platform: context.config.platform,\n shell: context.config.shell,\n version: context.config.version,\n },\n contexts: {\n cause: this.cause ? serializeError(this.cause) : undefined,\n app: {\n command: `${context.config.bin} ${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 what an error should\n * look like can be found here: {@link https://clig.dev/#errors}\n */\n render(): string {\n const rendered = dedent`\n ${this.header()}\n\n ${this.body()}\n `;\n\n const footer = this.footer();\n if (!footer) return rendered;\n\n return dedent`\n ${rendered}\n\n ${footer}\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 BaseError {\n isBug = IsBug.YES;\n\n constructor(override cause: Error) {\n super(\"GGT_CLI_UNEXPECTED_ERROR\", \"An unexpected error occurred\");\n }\n\n protected body(): string {\n return cleanStack(this.cause.stack ?? this.stack);\n }\n}\n\nexport class ClientError extends BaseError {\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 BaseError {\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 FlagError<T extends { name: string; char?: string } = { name: string; char?: string }> extends BaseError {\n isBug = IsBug.NO;\n\n #message: string;\n\n constructor(\n readonly flag: T,\n readonly description: string,\n ) {\n const name = flag.char ? `-${flag.char}, --${flag.name}` : `--${flag.name}`;\n super(\"GGT_CLI_FLAG_ERROR\", \"\");\n\n // oclif overwrites the message property, so we have to use different one...\n // https://github.com/oclif/core/blob/413592abca47ebedb2c006634a326bab325c26bd/src/parser/parse.ts#L317\n this.#message = `Invalid value provided for the ${name} flag`;\n }\n\n protected override header(): string {\n return `${this.code}: ${this.#message}`;\n }\n\n protected body(): string {\n return this.description;\n }\n}\n\nexport class InvalidSyncFileError extends BaseError {\n isBug = IsBug.MAYBE;\n\n constructor(\n override readonly cause: unknown,\n readonly sync: Sync,\n readonly app: string | undefined,\n ) {\n super(\"GGT_CLI_INVALID_SYNC_FILE\", \"The .gadget/sync.json file was invalid or not found\");\n }\n\n protected body(): string {\n return dedent`\n We failed to read the Gadget metadata file in this directory:\n\n ${this.sync.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 ?? \"<name of 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 ${this.sync.argv.join(\" \")} --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\nexport class InvalidSyncAppFlagError extends FlagError {\n constructor(sync: Sync) {\n super(\n { name: \"app\", char: \"a\" },\n dedent`\n You were about to sync the following app to the following directory:\n\n ${sync.flags.app} → ${sync.dir}\n\n However, that directory has already been synced with this app:\n\n ${sync.state.app}\n\n If you're sure that you want to sync \"${sync.flags.app}\" to \"${sync.dir}\", run \\`ggt sync\\` again with the \\`--force\\` flag:\n\n $ ggt sync ${sync.argv.join(\" \")} --force\n `,\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","cleanStack","randomUUID","RequestError","_","os","serializeError","baseSerializeError","dedent","inspect","context","BaseError","Error","capture","isBug","IsBug","NO","user","getUser","catch","noop","bind","getCurrentHub","captureException","event_id","sentryEventId","captureContext","id","String","email","username","name","undefined","tags","applicationId","app","arch","config","code","environment","env","value","platform","shell","version","contexts","cause","command","bin","process","argv","slice","join","device","hostname","family","type","runtime","release","flush","render","rendered","header","body","footer","message","YES","constructor","testLike","stack","captureStackTrace","error","serialized","isArray","AggregateError","method","options","url","URL","toJSON","response","MAYBE","UnexpectedError","ClientError","isGraphQLErrors","length","errors","uniqBy","output","i","isCloseEvent","isErrorEvent","isError","payload","reason","wasClean","YarnNotFoundError","FlagError","description","flag","char","InvalidSyncFileError","sync","dir","InvalidSyncAppFlagError","flags","state","e","isNil","isString","isNumber","isBoolean","every","locations","path"],"mappings":"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"}
|
|
@@ -24,12 +24,13 @@ export const app = Flags.custom({
|
|
|
24
24
|
--app https://my-app.gadget.app
|
|
25
25
|
--app https://my-app.gadget.app/edit
|
|
26
26
|
`);
|
|
27
|
-
const slug =
|
|
27
|
+
const slug = _.endsWith(parsed, "--development") ? parsed.slice(0, -"--development".length) : parsed;
|
|
28
28
|
const availableApps = await context.getAvailableApps();
|
|
29
|
-
const foundApp =
|
|
29
|
+
const foundApp = _.find(availableApps, (a)=>a.slug == slug);
|
|
30
30
|
if (foundApp) {
|
|
31
31
|
return foundApp.slug;
|
|
32
32
|
}
|
|
33
|
+
const sortedApps = _.sortBy(availableApps, (app)=>levenshtein.get(app.slug, slug));
|
|
33
34
|
throw new FlagError({
|
|
34
35
|
char: "a",
|
|
35
36
|
name: "app"
|
|
@@ -40,7 +41,7 @@ export const app = Flags.custom({
|
|
|
40
41
|
|
|
41
42
|
Did you mean one of these?
|
|
42
43
|
|
|
43
|
-
${_.
|
|
44
|
+
${_.map(sortedApps.slice(0, 10), (app)=>`* ${app.slug}`).join("\n")}
|
|
44
45
|
` : dedent`
|
|
45
46
|
Unknown application:
|
|
46
47
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/flags.ts"],"sourcesContent":["import { Flags } from \"@oclif/core\";\nimport levenshtein from \"fast-levenshtein\";\nimport _ from \"lodash\";\nimport { dedent } from \"ts-dedent\";\nimport { context } from \"./context.js\";\nimport { FlagError } from \"./errors.js\";\n\nexport const app = Flags.custom({\n char: \"a\",\n name: \"app\",\n summary: \"The Gadget application this command applies to.\",\n helpValue: \"<name>\",\n parse: async (value: string) => {\n const parsed = /^(https:\\/\\/)?(?<name>[\\w-]+)/.exec(value)?.groups?.[\"name\"];\n if (!parsed)\n throw new FlagError(\n { char: \"a\", name: \"app\" },\n dedent`\n The -a, --app flag must be the application's slug or URL\n\n Examples:\n\n --app my-app\n --app my-app.gadget.app\n --app https://my-app.gadget.app\n --app https://my-app.gadget.app/edit\n `,\n );\n\n const slug = _.endsWith(parsed, \"--development\") ? parsed.slice(0, -\"--development\".length) : parsed;\n\n const availableApps = await context.getAvailableApps();\n const foundApp = _.find(availableApps, (a) => a.slug == slug);\n if (foundApp) {\n return foundApp.slug;\n }\n\n const sortedApps = _.sortBy(availableApps, (app) => levenshtein.get(app.slug, slug));\n throw new FlagError(\n { char: \"a\", name: \"app\" },\n availableApps.length > 0\n ? dedent`\n Unknown application:\n\n ${value}\n\n Did you mean one of these?\n\n ${_.map(sortedApps.slice(0, 10), (app) => `* ${app.slug}`).join(\"\\n\")}\n `\n : dedent`\n Unknown application:\n\n ${value}\n\n It doesn't look like you have any applications.\n\n Visit https://gadget.new to create one!\n `,\n );\n },\n});\n"],"names":["Flags","levenshtein","_","dedent","context","FlagError","app","custom","char","name","summary","helpValue","parse","value","parsed","exec","groups","slug","endsWith","slice","length","availableApps","getAvailableApps","foundApp","find","a","sortedApps","sortBy","get","map","join"],"mappings":"AAAA,SAASA,KAAK,QAAQ,cAAc;AACpC,OAAOC,iBAAiB,mBAAmB;AAC3C,OAAOC,OAAO,SAAS;AACvB,SAASC,MAAM,QAAQ,YAAY;AACnC,SAASC,OAAO,QAAQ,eAAe;AACvC,SAASC,SAAS,QAAQ,cAAc;AAExC,OAAO,MAAMC,MAAMN,MAAMO,MAAM,CAAC;IAC9BC,MAAM;IACNC,MAAM;IACNC,SAAS;IACTC,WAAW;IACXC,OAAO,OAAOC;QACZ,MAAMC,SAAS,2CAAgCC,IAAI,CAACF,QAAQG,QAAQ,CAAC,OAAO;QAC5E,IAAI,CAACF,QACH,MAAM,IAAIT,UACR;YAAEG,MAAM;YAAKC,MAAM;QAAM,GACzBN,MAAM,CAAC;;;;;;;;;QASP,CAAC;QAGL,MAAMc,OAAOf,EAAEgB,QAAQ,CAACJ,QAAQ,mBAAmBA,OAAOK,KAAK,CAAC,GAAG,CAAC,gBAAgBC,MAAM,IAAIN;QAE9F,MAAMO,gBAAgB,MAAMjB,QAAQkB,gBAAgB;QACpD,MAAMC,WAAWrB,EAAEsB,IAAI,CAACH,eAAe,CAACI,IAAMA,EAAER,IAAI,IAAIA;QACxD,IAAIM,UAAU;YACZ,OAAOA,SAASN,IAAI;QACtB;QAEA,MAAMS,aAAaxB,EAAEyB,MAAM,CAACN,eAAe,CAACf,MAAQL,YAAY2B,GAAG,CAACtB,IAAIW,IAAI,EAAEA;QAC9E,MAAM,IAAIZ,UACR;YAAEG,MAAM;YAAKC,MAAM;QAAM,GACzBY,cAAcD,MAAM,GAAG,IACnBjB,MAAM,CAAC;;;gBAGD,EAAEU,MAAM;;;;gBAIR,EAAEX,EAAE2B,GAAG,CAACH,WAAWP,KAAK,CAAC,GAAG,KAAK,CAACb,MAAQ,CAAC,EAAE,EAAEA,IAAIW,IAAI,CAAC,CAAC,EAAEa,IAAI,CAAC,MAAM;YAC1E,CAAC,GACH3B,MAAM,CAAC;;;gBAGD,EAAEU,MAAM;;;;;YAKZ,CAAC;IAEX;AACF,GAAG"}
|
|
@@ -8,6 +8,10 @@ export class FSIgnorer {
|
|
|
8
8
|
ignores(filepath) {
|
|
9
9
|
const relative = path.isAbsolute(filepath) ? path.relative(this._rootDir, filepath) : filepath;
|
|
10
10
|
if (relative == "") return false;
|
|
11
|
+
// anything above the root dir is ignored
|
|
12
|
+
if (relative == "..") {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
11
15
|
return this._ignorer.ignores(relative);
|
|
12
16
|
}
|
|
13
17
|
reload() {
|
|
@@ -15,7 +19,7 @@ export class FSIgnorer {
|
|
|
15
19
|
this._ignorer.add(this._alwaysIgnore);
|
|
16
20
|
try {
|
|
17
21
|
this._ignorer.add(fs.readFileSync(this.filepath, "utf-8"));
|
|
18
|
-
debug("reloaded ignore rules from %s", this.filepath);
|
|
22
|
+
debug("reloaded ignore rules from %s", path.relative(this._rootDir, this.filepath));
|
|
19
23
|
} catch (error) {
|
|
20
24
|
ignoreEnoent(error);
|
|
21
25
|
}
|
|
@@ -93,7 +97,7 @@ export async function isEmptyDir(dir, opts = {
|
|
|
93
97
|
}
|
|
94
98
|
export function ignoreEnoent(error) {
|
|
95
99
|
if (error.code === "ENOENT") {
|
|
96
|
-
debug("ignoring ENOENT error %s", error.path);
|
|
100
|
+
debug("ignoring ENOENT error %s", path.basename(error.path));
|
|
97
101
|
return;
|
|
98
102
|
}
|
|
99
103
|
throw error;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/fs-utils.ts"],"sourcesContent":["import Debug from \"debug\";\nimport fs from \"fs-extra\";\nimport type { Ignore } from \"ignore\";\nimport ignore from \"ignore\";\nimport path from \"path\";\n\nconst debug = Debug(\"ggt:fs-utils\");\n\nexport class FSIgnorer {\n readonly filepath;\n\n private _ignorer!: Ignore;\n\n constructor(\n private readonly _rootDir: string,\n private readonly _alwaysIgnore: string[],\n ) {\n this.filepath = path.join(this._rootDir, \".ignore\");\n this.reload();\n }\n\n ignores(filepath: string): boolean {\n const relative = path.isAbsolute(filepath) ? path.relative(this._rootDir, filepath) : filepath;\n if (relative == \"\") return false;\n // anything above the root dir is ignored\n if (relative == \"..\") {\n return true;\n }\n return this._ignorer.ignores(relative);\n }\n\n reload(): void {\n this._ignorer = ignore.default();\n this._ignorer.add(this._alwaysIgnore);\n\n try {\n this._ignorer.add(fs.readFileSync(this.filepath, \"utf-8\"));\n debug(\"reloaded ignore rules from %s\", path.relative(this._rootDir, this.filepath));\n } catch (error) {\n ignoreEnoent(error);\n }\n }\n}\n\nexport interface WalkDirOptions {\n ignorer?: FSIgnorer;\n}\n\nexport async function* walkDir(dir: string, options: WalkDirOptions = {}): AsyncGenerator<string> {\n if (options.ignorer?.ignores(dir)) return;\n\n if (await isEmptyDir(dir)) {\n yield `${dir}/`;\n return;\n }\n\n for await (const entry of await fs.opendir(dir)) {\n const filepath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n yield* walkDir(filepath, options);\n } else if (entry.isFile() && !options.ignorer?.ignores(filepath)) {\n yield filepath;\n }\n }\n}\n\nexport function* walkDirSync(dir: string, options: WalkDirOptions = {}): Generator<string> {\n if (options.ignorer?.ignores(dir)) return;\n\n if (isEmptyDirSync(dir)) {\n yield `${dir}/`;\n return;\n }\n\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const filepath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n yield* walkDirSync(filepath, options);\n } else if (entry.isFile() && !options.ignorer?.ignores(filepath)) {\n yield filepath;\n }\n }\n}\n\nexport function isEmptyDirSync(dir: string, opts = { ignoreEnoent: true }): boolean {\n try {\n const files = fs.readdirSync(dir);\n return files.length === 0;\n } catch (error) {\n if (opts.ignoreEnoent) {\n ignoreEnoent(error);\n return true;\n }\n throw error;\n }\n}\n\nexport async function isEmptyDir(dir: string, opts = { ignoreEnoent: true }): Promise<boolean> {\n try {\n const files = await fs.readdir(dir);\n return files.length === 0;\n } catch (error) {\n if (opts.ignoreEnoent) {\n ignoreEnoent(error);\n return true;\n }\n throw error;\n }\n}\n\nexport function ignoreEnoent(error: any): void {\n if (error.code === \"ENOENT\") {\n debug(\"ignoring ENOENT error %s\", path.basename(error.path as string));\n return;\n }\n throw error;\n}\n"],"names":["Debug","fs","ignore","path","debug","FSIgnorer","ignores","filepath","relative","isAbsolute","_rootDir","_ignorer","reload","default","add","_alwaysIgnore","readFileSync","error","ignoreEnoent","constructor","join","walkDir","dir","options","ignorer","isEmptyDir","entry","opendir","name","isDirectory","isFile","walkDirSync","isEmptyDirSync","readdirSync","withFileTypes","opts","files","length","readdir","code","basename"],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ class CommandHelp extends OclifCommandHelp {
|
|
|
26
26
|
if (_.isString(examples)) {
|
|
27
27
|
return examples;
|
|
28
28
|
}
|
|
29
|
-
if (
|
|
29
|
+
if (_.isArray(examples) && _.every(examples, _.isString)) {
|
|
30
30
|
return examples.join("\n\n");
|
|
31
31
|
}
|
|
32
32
|
return super.examples(examples);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/help.ts"],"sourcesContent":["import type { Command } from \"@oclif/core\";\nimport { CommandHelp as OclifCommandHelp, Help as OclifHelp } from \"@oclif/core\";\nimport _ from \"lodash\";\n\nexport default class Help extends OclifHelp {\n override CommandHelpClass = CommandHelp;\n}\n\nclass CommandHelp extends OclifCommandHelp {\n /**\n * By default, oclif tries to format the description so that it fit's within the terminal window. However, if the description is already\n * formatted with `dedent`, then the description gets mangled and the help output is not pretty.\n *\n * This overrides the default behavior to just use the description as-is if it already exists.\n */\n protected override description(): string | undefined {\n if (this.command.description) {\n return this.command.description;\n }\n return super.description();\n }\n\n /**\n * Same as above, but for examples.\n */\n protected override examples(examples: string | string[] | Command.Example[] | undefined): string | undefined {\n if (_.isString(examples)) {\n return examples;\n }\n if (_.isArray(examples) && _.every(examples, _.isString)) {\n return examples.join(\"\\n\\n\");\n }\n return super.examples(examples);\n }\n}\n"],"names":["CommandHelp","OclifCommandHelp","Help","OclifHelp","_","CommandHelpClass","description","command","examples","isString","isArray","every","join"],"mappings":";AACA,SAASA,eAAeC,gBAAgB,EAAEC,QAAQC,SAAS,QAAQ,cAAc;AACjF,OAAOC,OAAO,SAAS;AAER,MAAMF,aAAaC;;;QAChC,uBAASE,oBAAmBL;;AAC9B;AAFA,SAAqBE,kBAEpB;AAED,MAAMF,oBAAoBC;IACxB;;;;;GAKC,GACD,AAAmBK,cAAkC;QACnD,IAAI,IAAI,CAACC,OAAO,CAACD,WAAW,EAAE;YAC5B,OAAO,IAAI,CAACC,OAAO,CAACD,WAAW;QACjC;QACA,OAAO,KAAK,CAACA;IACf;IAEA;;GAEC,GACD,AAAmBE,SAASA,QAA2D,EAAsB;QAC3G,IAAIJ,EAAEK,QAAQ,CAACD,WAAW;YACxB,OAAOA;QACT;QACA,IAAIJ,EAAEM,OAAO,CAACF,aAAaJ,EAAEO,KAAK,CAACH,UAAUJ,EAAEK,QAAQ,GAAG;YACxD,OAAOD,SAASI,IAAI,CAAC;QACvB;QACA,OAAO,KAAK,CAACJ,SAASA;IACxB;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/promise.ts"],"sourcesContent":["/**\n * Long lived references to Promises stress the garbage collector in JS. Instead of caching resolved Promises, we cache\n * these little data objects instead which reference the resolution or rejection of the Promise, allowing the Promise\n * object to be free'd.\n */\nexport class PromiseWrapper<T> {\n resolution?: T;\n rejection?: any;\n pendingPromise?: Promise<T>;\n\n constructor(promise: Promise<T>) {\n this.pendingPromise = promise;\n\n promise\n .then((res) => {\n this.resolution = res;\n return res;\n })\n .catch((err) => {\n this.rejection = err;\n })\n .finally(() => {\n delete this.pendingPromise;\n });\n }\n\n async unwrap(): Promise<T> {\n if (this.resolution) {\n return this.resolution;\n } else if (this.rejection) {\n throw this.rejection;\n } else {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return await this.pendingPromise!;\n }\n }\n}\n\n/**\n * A promise that can be resolved or rejected from outside its callback.\n *\n * This is typically used when you want to await a promise that is resolved or rejected from outside the current scope,\n * such as from an event handler.\n *\n * @example\n * const signal = new PromiseSignal();\n * process.on(\"SIGINT\", () => {\n * signal.resolve();\n * });\n * await signal;\n */\nexport class PromiseSignal<T = void> implements Promise<T> {\n readonly [Symbol.toStringTag]!: string;\n\n resolve!: (value: T | PromiseLike<T>) => void;\n reject!: (reason?: any) => void;\n\n private _promise: PromiseWrapper<T>;\n\n constructor() {\n this._promise = new PromiseWrapper<T>(\n new Promise((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n }),\n );\n\n this[Symbol.toStringTag] = String(this._promise.pendingPromise);\n }\n\n then<R = T, E = never>(onfulfilled?: (value: T) => R | PromiseLike<R>, onrejected?: (reason: any) => E | PromiseLike<E>): Promise<R | E> {\n return this._promise.unwrap().then(onfulfilled, onrejected);\n }\n\n catch<E = never>(onrejected?: (reason: any) => E | PromiseLike<E>): Promise<T | E> {\n return this._promise.unwrap().catch(onrejected);\n }\n\n finally(onfinally?: () => void): Promise<T> {\n return this._promise.unwrap().finally(onfinally);\n }\n}\n"],"names":["PromiseWrapper","unwrap","resolution","rejection","pendingPromise","constructor","promise","then","res","catch","err","finally","Symbol","toStringTag","PromiseSignal","onfulfilled","onrejected","_promise","onfinally","resolve","reject","Promise","String"],"mappings":"AAAA;;;;CAIC;AACD,OAAO,MAAMA;IAqBX,MAAMC,SAAqB;QACzB,IAAI,IAAI,CAACC,UAAU,EAAE;YACnB,OAAO,IAAI,CAACA,UAAU;QACxB,OAAO,IAAI,IAAI,CAACC,SAAS,EAAE;YACzB,MAAM,IAAI,CAACA,SAAS;QACtB,OAAO;YACL,oEAAoE;YACpE,OAAO,MAAM,IAAI,CAACC,cAAc;QAClC;IACF;IAzBAC,YAAYC,OAAmB,CAAE;QAJjCJ,uBAAAA,cAAAA,KAAAA;QACAC,uBAAAA,aAAAA,KAAAA;QACAC,uBAAAA,kBAAAA,KAAAA;QAGE,IAAI,CAACA,cAAc,GAAGE;QAEtBA,QACGC,IAAI,CAAC,CAACC;YACL,IAAI,CAACN,UAAU,GAAGM;YAClB,OAAOA;QACT,GACCC,KAAK,CAAC,CAACC;YACN,IAAI,CAACP,SAAS,GAAGO;QACnB,GACCC,OAAO,CAAC;YACP,OAAO,IAAI,CAACP,cAAc;QAC5B;IACJ;AAYF;IAgBYQ,sBAAAA,OAAOC,WAAW;AAd9B;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC;IAmBXP,KAAuBQ,WAA8C,EAAEC,UAAgD,EAAkB;QACvI,OAAO,IAAI,CAACC,QAAQ,CAAChB,MAAM,GAAGM,IAAI,CAACQ,aAAaC;IAClD;IAEAP,MAAiBO,UAAgD,EAAkB;QACjF,OAAO,IAAI,CAACC,QAAQ,CAAChB,MAAM,GAAGQ,KAAK,CAACO;IACtC;IAEAL,QAAQO,SAAsB,EAAc;QAC1C,OAAO,IAAI,CAACD,QAAQ,CAAChB,MAAM,GAAGU,OAAO,CAACO;IACxC;IArBAb,aAAc;QAPd,uBAAUO,qBAAV,KAAA;QAEAO,uBAAAA,WAAAA,KAAAA;QACAC,uBAAAA,UAAAA,KAAAA;QAEA,uBAAQH,YAAR,KAAA;QAGE,IAAI,CAACA,QAAQ,GAAG,IAAIjB,eAClB,IAAIqB,QAAQ,CAACF,SAASC;YACpB,IAAI,CAACD,OAAO,GAAGA;YACf,IAAI,CAACC,MAAM,GAAGA;QAChB;QAGF,IAAI,CAACR,OAAOC,WAAW,CAAC,GAAGS,OAAO,IAAI,CAACL,QAAQ,CAACb,cAAc;IAChE;AAaF"}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export function sleep(ms = 0) {
|
|
2
2
|
return new Promise((resolve)=>ms == 0 ? setImmediate(resolve) : setTimeout(resolve, ms));
|
|
3
3
|
}
|
|
4
|
-
export async function sleepUntil(fn, { interval =0
|
|
4
|
+
export async function sleepUntil(fn, { interval = 0, timeout = 5_000 } = {}) {
|
|
5
|
+
if (process.env["CI"]) {
|
|
6
|
+
// double the timeout in CI to account for slower machines
|
|
7
|
+
timeout *= 2;
|
|
8
|
+
}
|
|
5
9
|
const start = isFinite(timeout) && Date.now();
|
|
6
|
-
// eslint-disable-next-line no-constant-condition
|
|
10
|
+
// eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition
|
|
7
11
|
while(true){
|
|
8
12
|
if (fn()) return;
|
|
9
13
|
await sleep(interval);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/sleep.ts"],"sourcesContent":["export function sleep(ms = 0): Promise<void> {\n return new Promise((resolve) => (ms == 0 ? setImmediate(resolve) : setTimeout(resolve, ms)));\n}\n\nexport async function sleepUntil(fn: () => boolean, { interval = 0, timeout = 5_000 } = {}): Promise<void> {\n if (process.env[\"CI\"]) {\n // double the timeout in CI to account for slower machines\n timeout *= 2;\n }\n\n const start = isFinite(timeout) && Date.now();\n\n // eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition\n while (true) {\n if (fn()) return;\n await sleep(interval);\n\n if (start && Date.now() - start > timeout) {\n const error = new Error(`Timed out after ${timeout} milliseconds`);\n Error.captureStackTrace(error, sleepUntil);\n throw error;\n }\n }\n}\n"],"names":["sleep","ms","Promise","resolve","setImmediate","setTimeout","sleepUntil","fn","interval","timeout","process","env","start","isFinite","Date","now","error","Error","captureStackTrace"],"mappings":"AAAA,OAAO,SAASA,MAAMC,KAAK,CAAC;IAC1B,OAAO,IAAIC,QAAQ,CAACC,UAAaF,MAAM,IAAIG,aAAaD,WAAWE,WAAWF,SAASF;AACzF;AAEA,OAAO,eAAeK,WAAWC,EAAiB,EAAE,EAAEC,WAAW,CAAC,EAAEC,UAAU,KAAK,EAAE,GAAG,CAAC,CAAC;IACxF,IAAIC,QAAQC,GAAG,CAAC,KAAK,EAAE;QACrB,0DAA0D;QAC1DF,WAAW;IACb;IAEA,MAAMG,QAAQC,SAASJ,YAAYK,KAAKC,GAAG;IAE3C,8FAA8F;IAC9F,MAAO,KAAM;QACX,IAAIR,MAAM;QACV,MAAMP,MAAMQ;QAEZ,IAAII,SAASE,KAAKC,GAAG,KAAKH,QAAQH,SAAS;YACzC,MAAMO,QAAQ,IAAIC,MAAM,CAAC,gBAAgB,EAAER,QAAQ,aAAa,CAAC;YACjEQ,MAAMC,iBAAiB,CAACF,OAAOV;YAC/B,MAAMU;QACR;IACF;AACF"}
|