@gadgetinc/ggt 0.2.4 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -91
- package/bin/dev.js +11 -19
- package/bin/run.js +1 -9
- package/lib/__generated__/graphql.js +2 -2
- package/lib/__generated__/graphql.js.map +1 -1
- package/lib/commands/index.js +9 -0
- package/lib/commands/index.js.map +1 -0
- package/lib/commands/list.js +34 -49
- package/lib/commands/list.js.map +1 -1
- package/lib/commands/login.js +74 -17
- package/lib/commands/login.js.map +1 -1
- package/lib/commands/logout.js +19 -23
- package/lib/commands/logout.js.map +1 -1
- package/lib/commands/root.js +85 -0
- package/lib/commands/root.js.map +1 -0
- package/lib/commands/sync.js +319 -685
- package/lib/commands/sync.js.map +1 -1
- package/lib/commands/whoami.js +23 -27
- package/lib/commands/whoami.js.map +1 -1
- package/lib/main.js +12 -0
- package/lib/main.js.map +1 -0
- package/lib/services/app.js +36 -0
- package/lib/services/app.js.map +1 -0
- package/lib/services/args.js +28 -0
- package/lib/services/args.js.map +1 -0
- package/lib/services/config.js +139 -0
- package/lib/services/config.js.map +1 -0
- package/lib/services/edit-graphql.js +193 -0
- package/lib/services/edit-graphql.js.map +1 -0
- package/lib/services/errors.js +64 -89
- package/lib/services/errors.js.map +1 -1
- package/lib/services/filesync.js +404 -0
- package/lib/services/filesync.js.map +1 -0
- package/lib/services/fs-utils.js +18 -91
- package/lib/services/fs-utils.js.map +1 -1
- package/lib/services/http.js +53 -0
- package/lib/services/http.js.map +1 -0
- package/lib/services/log.js +45 -0
- package/lib/services/log.js.map +1 -0
- package/lib/services/notify.js +30 -0
- package/lib/services/notify.js.map +1 -0
- package/lib/services/output.js +59 -0
- package/lib/services/output.js.map +1 -0
- package/lib/services/promise.js +8 -5
- package/lib/services/promise.js.map +1 -1
- package/lib/services/session.js +27 -0
- package/lib/services/session.js.map +1 -0
- package/lib/services/sleep.js +2 -5
- package/lib/services/sleep.js.map +1 -1
- package/lib/services/timeout.js +8 -0
- package/lib/services/timeout.js.map +1 -0
- package/lib/services/user.js +70 -0
- package/lib/services/user.js.map +1 -0
- package/lib/services/version.js +72 -0
- package/lib/services/version.js.map +1 -0
- package/npm-shrinkwrap.json +15680 -25737
- package/package.json +53 -60
- package/lib/commands/help.js +0 -28
- package/lib/commands/help.js.map +0 -1
- package/lib/index.js +0 -3
- package/lib/index.js.map +0 -1
- package/lib/services/base-command.js +0 -203
- package/lib/services/base-command.js.map +0 -1
- package/lib/services/client.js +0 -209
- package/lib/services/client.js.map +0 -1
- package/lib/services/context.js +0 -143
- package/lib/services/context.js.map +0 -1
- package/lib/services/flags.js +0 -57
- package/lib/services/flags.js.map +0 -1
- package/lib/services/help.js +0 -36
- package/lib/services/help.js.map +0 -1
- package/oclif.manifest.json +0 -244
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/http.ts"],"sourcesContent":["import { got, HTTPError, type OptionsInit } from \"got\";\nimport { config } from \"./config.js\";\nimport { createLogger } from \"./log.js\";\nimport { readSession, writeSession } from \"./session.js\";\n\nconst log = createLogger(\"http\");\n\nexport const http = got.extend({\n hooks: {\n beforeRequest: [\n (options) => {\n options.headers[\"user-agent\"] = config.versionFull;\n log.info(\"http request\", {\n method: options.method,\n url: options.url?.toString(),\n });\n },\n ],\n afterResponse: [\n (response) => {\n log.info(\"http response\", {\n method: response.request.options.method,\n url: response.request.options.url?.toString(),\n statusCode: response.statusCode,\n traceId: response.headers[\"x-trace-id\"],\n });\n\n if (response.statusCode === 401 && isGadgetRequest(response.request.options)) {\n writeSession(undefined);\n }\n return response;\n },\n ],\n },\n});\n\nexport const isUnauthorized = (error: unknown): boolean => {\n return error instanceof HTTPError && error.response.statusCode === 401 && isGadgetRequest(error.request.options);\n};\n\nexport const swallowUnauthorized = (error: unknown): void => {\n if (isUnauthorized(error)) {\n log.warn(\"swallowing unauthorized error\", { error });\n return;\n }\n throw error;\n};\n\nexport const loadCookie = (): string | undefined => {\n const token = readSession();\n return token && `session=${encodeURIComponent(token)};`;\n};\n\nconst isGadgetRequest = (options: OptionsInit) => {\n return options.url instanceof URL && options.url.host === config.domains.services;\n};\n"],"names":["got","HTTPError","config","createLogger","readSession","writeSession","log","http","extend","hooks","beforeRequest","options","headers","versionFull","info","method","url","toString","afterResponse","response","request","statusCode","traceId","isGadgetRequest","undefined","isUnauthorized","error","swallowUnauthorized","warn","loadCookie","token","encodeURIComponent","URL","host","domains","services"],"mappings":"AAAA,SAASA,GAAG,EAAEC,SAAS,QAA0B,MAAM;AACvD,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,YAAY,QAAQ,WAAW;AACxC,SAASC,WAAW,EAAEC,YAAY,QAAQ,eAAe;AAEzD,MAAMC,MAAMH,aAAa;AAEzB,OAAO,MAAMI,OAAOP,IAAIQ,MAAM,CAAC;IAC7BC,OAAO;QACLC,eAAe;YACb,CAACC;gBACCA,QAAQC,OAAO,CAAC,aAAa,GAAGV,OAAOW,WAAW;gBAClDP,IAAIQ,IAAI,CAAC,gBAAgB;oBACvBC,QAAQJ,QAAQI,MAAM;oBACtBC,KAAKL,QAAQK,GAAG,EAAEC;gBACpB;YACF;SACD;QACDC,eAAe;YACb,CAACC;gBACCb,IAAIQ,IAAI,CAAC,iBAAiB;oBACxBC,QAAQI,SAASC,OAAO,CAACT,OAAO,CAACI,MAAM;oBACvCC,KAAKG,SAASC,OAAO,CAACT,OAAO,CAACK,GAAG,EAAEC;oBACnCI,YAAYF,SAASE,UAAU;oBAC/BC,SAASH,SAASP,OAAO,CAAC,aAAa;gBACzC;gBAEA,IAAIO,SAASE,UAAU,KAAK,OAAOE,gBAAgBJ,SAASC,OAAO,CAACT,OAAO,GAAG;oBAC5EN,aAAamB;gBACf;gBACA,OAAOL;YACT;SACD;IACH;AACF,GAAG;AAEH,OAAO,MAAMM,iBAAiB,CAACC;IAC7B,OAAOA,iBAAiBzB,aAAayB,MAAMP,QAAQ,CAACE,UAAU,KAAK,OAAOE,gBAAgBG,MAAMN,OAAO,CAACT,OAAO;AACjH,EAAE;AAEF,OAAO,MAAMgB,sBAAsB,CAACD;IAClC,IAAID,eAAeC,QAAQ;QACzBpB,IAAIsB,IAAI,CAAC,iCAAiC;YAAEF;QAAM;QAClD;IACF;IACA,MAAMA;AACR,EAAE;AAEF,OAAO,MAAMG,aAAa;IACxB,MAAMC,QAAQ1B;IACd,OAAO0B,SAAS,CAAC,QAAQ,EAAEC,mBAAmBD,OAAO,CAAC,CAAC;AACzD,EAAE;AAEF,MAAMP,kBAAkB,CAACZ;IACvB,OAAOA,QAAQK,GAAG,YAAYgB,OAAOrB,QAAQK,GAAG,CAACiB,IAAI,KAAK/B,OAAOgC,OAAO,CAACC,QAAQ;AACnF"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-confusing-void-expression */ import { addBreadcrumb as addSentryBreadcrumb } from "@sentry/node";
|
|
2
|
+
import Debug from "debug";
|
|
3
|
+
import _ from "lodash";
|
|
4
|
+
import { serializeError } from "./errors.js";
|
|
5
|
+
let longestName = 0;
|
|
6
|
+
let longestMessage = 0;
|
|
7
|
+
export const createLogger = (name, fields = {})=>{
|
|
8
|
+
longestName = Math.max(longestName, name.length);
|
|
9
|
+
const baseFields = _.isFunction(fields) ? fields : ()=>fields;
|
|
10
|
+
const createLog = (level)=>{
|
|
11
|
+
const debug = Debug(`ggt:${name}`);
|
|
12
|
+
return (msg, fields)=>{
|
|
13
|
+
longestMessage = Math.max(longestMessage, msg.length);
|
|
14
|
+
fields = {
|
|
15
|
+
...baseFields(),
|
|
16
|
+
...fields
|
|
17
|
+
};
|
|
18
|
+
if ("error" in fields) {
|
|
19
|
+
fields.error = serializeError(fields.error);
|
|
20
|
+
}
|
|
21
|
+
if (_.isEmpty(fields)) {
|
|
22
|
+
debug("%s%s", _.repeat(" ", longestName - name.length), _.padEnd(msg, longestMessage));
|
|
23
|
+
} else {
|
|
24
|
+
debug("%s%s %o", _.repeat(" ", longestName - name.length), _.padEnd(msg, longestMessage), fields);
|
|
25
|
+
}
|
|
26
|
+
if (level === "debug") {
|
|
27
|
+
// don't send debug logs to Sentry
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
addSentryBreadcrumb({
|
|
31
|
+
level: level === "warn" ? "warning" : level,
|
|
32
|
+
message: msg,
|
|
33
|
+
data: fields
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
debug: createLog("debug"),
|
|
39
|
+
info: createLog("info"),
|
|
40
|
+
warn: createLog("warn"),
|
|
41
|
+
error: createLog("error")
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
//# sourceMappingURL=log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/log.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-confusing-void-expression */\nimport { addBreadcrumb as addSentryBreadcrumb } from \"@sentry/node\";\nimport Debug from \"debug\";\nimport _ from \"lodash\";\nimport type { Jsonifiable } from \"type-fest\";\nimport { serializeError } from \"./errors.js\";\n\ntype JsonifiableObject = { [Key in string]?: Jsonifiable } | { toJSON: () => Jsonifiable } | { error?: unknown };\n\nlet longestName = 0;\nlet longestMessage = 0;\n\nexport const createLogger = (name: string, fields: JsonifiableObject | (() => JsonifiableObject) = {}) => {\n longestName = Math.max(longestName, name.length);\n const baseFields = _.isFunction(fields) ? fields : () => fields;\n\n const createLog = (level: \"debug\" | \"info\" | \"warn\" | \"error\") => {\n const debug = Debug(`ggt:${name}`);\n\n return (msg: Lowercase<string>, fields?: JsonifiableObject) => {\n longestMessage = Math.max(longestMessage, msg.length);\n fields = { ...baseFields(), ...fields };\n if (\"error\" in fields) {\n fields.error = serializeError(fields.error);\n }\n\n if (_.isEmpty(fields)) {\n debug(\"%s%s\", _.repeat(\" \", longestName - name.length), _.padEnd(msg, longestMessage));\n } else {\n debug(\"%s%s %o\", _.repeat(\" \", longestName - name.length), _.padEnd(msg, longestMessage), fields);\n }\n\n if (level === \"debug\") {\n // don't send debug logs to Sentry\n return;\n }\n\n addSentryBreadcrumb({\n level: level === \"warn\" ? \"warning\" : level,\n message: msg,\n data: fields,\n });\n };\n };\n\n return {\n debug: createLog(\"debug\"),\n info: createLog(\"info\"),\n warn: createLog(\"warn\"),\n error: createLog(\"error\"),\n };\n};\n"],"names":["addBreadcrumb","addSentryBreadcrumb","Debug","_","serializeError","longestName","longestMessage","createLogger","name","fields","Math","max","length","baseFields","isFunction","createLog","level","debug","msg","error","isEmpty","repeat","padEnd","message","data","info","warn"],"mappings":"AAAA,kEAAkE,GAClE,SAASA,iBAAiBC,mBAAmB,QAAQ,eAAe;AACpE,OAAOC,WAAW,QAAQ;AAC1B,OAAOC,OAAO,SAAS;AAEvB,SAASC,cAAc,QAAQ,cAAc;AAI7C,IAAIC,cAAc;AAClB,IAAIC,iBAAiB;AAErB,OAAO,MAAMC,eAAe,CAACC,MAAcC,SAAwD,CAAC,CAAC;IACnGJ,cAAcK,KAAKC,GAAG,CAACN,aAAaG,KAAKI,MAAM;IAC/C,MAAMC,aAAaV,EAAEW,UAAU,CAACL,UAAUA,SAAS,IAAMA;IAEzD,MAAMM,YAAY,CAACC;QACjB,MAAMC,QAAQf,MAAM,CAAC,IAAI,EAAEM,KAAK,CAAC;QAEjC,OAAO,CAACU,KAAwBT;YAC9BH,iBAAiBI,KAAKC,GAAG,CAACL,gBAAgBY,IAAIN,MAAM;YACpDH,SAAS;gBAAE,GAAGI,YAAY;gBAAE,GAAGJ,MAAM;YAAC;YACtC,IAAI,WAAWA,QAAQ;gBACrBA,OAAOU,KAAK,GAAGf,eAAeK,OAAOU,KAAK;YAC5C;YAEA,IAAIhB,EAAEiB,OAAO,CAACX,SAAS;gBACrBQ,MAAM,QAAQd,EAAEkB,MAAM,CAAC,KAAKhB,cAAcG,KAAKI,MAAM,GAAGT,EAAEmB,MAAM,CAACJ,KAAKZ;YACxE,OAAO;gBACLW,MAAM,WAAWd,EAAEkB,MAAM,CAAC,KAAKhB,cAAcG,KAAKI,MAAM,GAAGT,EAAEmB,MAAM,CAACJ,KAAKZ,iBAAiBG;YAC5F;YAEA,IAAIO,UAAU,SAAS;gBACrB,kCAAkC;gBAClC;YACF;YAEAf,oBAAoB;gBAClBe,OAAOA,UAAU,SAAS,YAAYA;gBACtCO,SAASL;gBACTM,MAAMf;YACR;QACF;IACF;IAEA,OAAO;QACLQ,OAAOF,UAAU;QACjBU,MAAMV,UAAU;QAChBW,MAAMX,UAAU;QAChBI,OAAOJ,UAAU;IACnB;AACF,EAAE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import notifier from "node-notifier";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { workspaceRoot } from "./config.js";
|
|
4
|
+
import { createLogger } from "./log.js";
|
|
5
|
+
const log = createLogger("notify");
|
|
6
|
+
/**
|
|
7
|
+
* Sends a native OS notification to the user.
|
|
8
|
+
*
|
|
9
|
+
* @see {@link https://www.npmjs.com/package/node-notifier node-notifier}
|
|
10
|
+
*/ export const notify = (notification)=>{
|
|
11
|
+
log.info("notifying user", {
|
|
12
|
+
notification: notification
|
|
13
|
+
});
|
|
14
|
+
notifier.notify({
|
|
15
|
+
title: "Gadget",
|
|
16
|
+
contentImage: path.join(workspaceRoot, "assets/favicon-128@4x.png"),
|
|
17
|
+
icon: path.join(workspaceRoot, "assets/favicon-128@4x.png"),
|
|
18
|
+
sound: true,
|
|
19
|
+
timeout: false,
|
|
20
|
+
...notification
|
|
21
|
+
}, (error)=>{
|
|
22
|
+
if (error) {
|
|
23
|
+
log.warn("error notifying user", {
|
|
24
|
+
notification: notification
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=notify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/notify.ts"],"sourcesContent":["import notifier, { type Notification } from \"node-notifier\";\nimport type WindowsBalloon from \"node-notifier/notifiers/balloon.js\";\nimport type Growl from \"node-notifier/notifiers/growl.js\";\nimport type NotificationCenter from \"node-notifier/notifiers/notificationcenter.js\";\nimport type NotifySend from \"node-notifier/notifiers/notifysend.js\";\nimport type WindowsToaster from \"node-notifier/notifiers/toaster.js\";\nimport path from \"node:path\";\nimport type { Jsonifiable } from \"type-fest\";\nimport { workspaceRoot } from \"./config.js\";\nimport { createLogger } from \"./log.js\";\n\nconst log = createLogger(\"notify\");\n\n/**\n * Sends a native OS notification to the user.\n *\n * @see {@link https://www.npmjs.com/package/node-notifier node-notifier}\n */\nexport const notify = (\n notification:\n | Notification\n | NotificationCenter.Notification\n | NotifySend.Notification\n | WindowsToaster.Notification\n | WindowsBalloon.Notification\n | Growl.Notification,\n) => {\n log.info(\"notifying user\", { notification: notification as Jsonifiable });\n\n notifier.notify(\n {\n title: \"Gadget\",\n contentImage: path.join(workspaceRoot, \"assets/favicon-128@4x.png\"),\n icon: path.join(workspaceRoot, \"assets/favicon-128@4x.png\"),\n sound: true,\n timeout: false,\n ...notification,\n },\n (error) => {\n if (error) {\n log.warn(\"error notifying user\", { notification: notification as Jsonifiable });\n }\n },\n );\n};\n"],"names":["notifier","path","workspaceRoot","createLogger","log","notify","notification","info","title","contentImage","join","icon","sound","timeout","error","warn"],"mappings":"AAAA,OAAOA,cAAqC,gBAAgB;AAM5D,OAAOC,UAAU,YAAY;AAE7B,SAASC,aAAa,QAAQ,cAAc;AAC5C,SAASC,YAAY,QAAQ,WAAW;AAExC,MAAMC,MAAMD,aAAa;AAEzB;;;;CAIC,GACD,OAAO,MAAME,SAAS,CACpBC;IAQAF,IAAIG,IAAI,CAAC,kBAAkB;QAAED,cAAcA;IAA4B;IAEvEN,SAASK,MAAM,CACb;QACEG,OAAO;QACPC,cAAcR,KAAKS,IAAI,CAACR,eAAe;QACvCS,MAAMV,KAAKS,IAAI,CAACR,eAAe;QAC/BU,OAAO;QACPC,SAAS;QACT,GAAGP,YAAY;IACjB,GACA,CAACQ;QACC,IAAIA,OAAO;YACTV,IAAIW,IAAI,CAAC,wBAAwB;gBAAET,cAAcA;YAA4B;QAC/E;IACF;AAEJ,EAAE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
2
|
+
import chalkTemplate from "chalk-template";
|
|
3
|
+
import levenshtein from "fast-levenshtein";
|
|
4
|
+
import _ from "lodash";
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
import process from "node:process";
|
|
7
|
+
import { dedent } from "ts-dedent";
|
|
8
|
+
/**
|
|
9
|
+
* A wrapper around process.stdout and process.stderr that allows us to mock out the streams for testing.
|
|
10
|
+
*
|
|
11
|
+
* @see https://github.com/oclif/core/blob/16139fe8a7f991b4b446a1599ab63f15d9809b8e/src/cli-ux/stream.ts
|
|
12
|
+
*/ export class Stream {
|
|
13
|
+
get isTTY() {
|
|
14
|
+
return process[this.channel].isTTY;
|
|
15
|
+
}
|
|
16
|
+
getWindowSize() {
|
|
17
|
+
return process[this.channel].getWindowSize();
|
|
18
|
+
}
|
|
19
|
+
write(data) {
|
|
20
|
+
return process[this.channel].write(data);
|
|
21
|
+
}
|
|
22
|
+
on(event, listener) {
|
|
23
|
+
process[this.channel].on(event, listener);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
once(event, listener) {
|
|
27
|
+
process[this.channel].once(event, listener);
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
constructor(channel){
|
|
31
|
+
_define_property(this, "channel", void 0);
|
|
32
|
+
this.channel = channel;
|
|
33
|
+
process[this.channel].on("error", (err)=>{
|
|
34
|
+
if (err.code === "EPIPE") {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export const stdout = new Stream("stdout");
|
|
42
|
+
export const stderr = new Stream("stderr");
|
|
43
|
+
export const sprint = (template, ...values)=>{
|
|
44
|
+
const content = _.isString(template) ? template : chalkTemplate(template, ...values);
|
|
45
|
+
return dedent(content);
|
|
46
|
+
};
|
|
47
|
+
export const print = (template, ...values)=>{
|
|
48
|
+
stdout.write(sprint(template, ...values));
|
|
49
|
+
};
|
|
50
|
+
export const println = (template, ...values)=>{
|
|
51
|
+
if (template) stdout.write(sprint(template, ...values));
|
|
52
|
+
stdout.write("\n");
|
|
53
|
+
};
|
|
54
|
+
export const sortByLevenshtein = (input, options)=>{
|
|
55
|
+
assert(options.length > 0, "options must not be empty");
|
|
56
|
+
return _.sortBy(options, (opt)=>levenshtein.get(opt, input));
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/output.ts"],"sourcesContent":["import chalkTemplate from \"chalk-template\";\nimport levenshtein from \"fast-levenshtein\";\nimport _ from \"lodash\";\nimport assert from \"node:assert\";\nimport process from \"node:process\";\nimport { dedent } from \"ts-dedent\";\n\n/**\n * A wrapper around process.stdout and process.stderr that allows us to mock out the streams for testing.\n *\n * @see https://github.com/oclif/core/blob/16139fe8a7f991b4b446a1599ab63f15d9809b8e/src/cli-ux/stream.ts\n */\nexport class Stream {\n public constructor(public channel: \"stdout\" | \"stderr\") {\n process[this.channel].on(\"error\", (err) => {\n if (err.code === \"EPIPE\") {\n return;\n }\n throw err;\n });\n }\n\n public get isTTY(): boolean {\n return process[this.channel].isTTY;\n }\n\n public getWindowSize(): number[] {\n return process[this.channel].getWindowSize();\n }\n\n public write(data: string): boolean {\n return process[this.channel].write(data);\n }\n\n public on(event: string, listener: (...args: any[]) => void): this {\n process[this.channel].on(event, listener);\n return this;\n }\n\n public once(event: string, listener: (...args: any[]) => void): this {\n process[this.channel].once(event, listener);\n return this;\n }\n}\n\nexport const stdout = new Stream(\"stdout\");\nexport const stderr = new Stream(\"stderr\");\n\nexport const sprint = (template: TemplateStringsArray | string, ...values: unknown[]) => {\n const content = _.isString(template) ? template : chalkTemplate(template, ...values);\n return dedent(content);\n};\n\nexport const print = (template: TemplateStringsArray | string, ...values: unknown[]) => {\n stdout.write(sprint(template, ...values));\n};\n\nexport const println = (template?: TemplateStringsArray | string, ...values: unknown[]) => {\n if (template) stdout.write(sprint(template, ...values));\n stdout.write(\"\\n\");\n};\n\nexport const sortByLevenshtein = (input: string, options: readonly string[]): [closest: string, ...sorted: string[]] => {\n assert(options.length > 0, \"options must not be empty\");\n return _.sortBy(options, (opt) => levenshtein.get(opt, input)) as [string, ...string[]];\n};\n"],"names":["chalkTemplate","levenshtein","_","assert","process","dedent","Stream","isTTY","channel","getWindowSize","write","data","on","event","listener","once","err","code","stdout","stderr","sprint","template","values","content","isString","print","println","sortByLevenshtein","input","options","length","sortBy","opt","get"],"mappings":";AAAA,OAAOA,mBAAmB,iBAAiB;AAC3C,OAAOC,iBAAiB,mBAAmB;AAC3C,OAAOC,OAAO,SAAS;AACvB,OAAOC,YAAY,cAAc;AACjC,OAAOC,aAAa,eAAe;AACnC,SAASC,MAAM,QAAQ,YAAY;AAEnC;;;;CAIC,GACD,OAAO,MAAMC;IAUX,IAAWC,QAAiB;QAC1B,OAAOH,OAAO,CAAC,IAAI,CAACI,OAAO,CAAC,CAACD,KAAK;IACpC;IAEOE,gBAA0B;QAC/B,OAAOL,OAAO,CAAC,IAAI,CAACI,OAAO,CAAC,CAACC,aAAa;IAC5C;IAEOC,MAAMC,IAAY,EAAW;QAClC,OAAOP,OAAO,CAAC,IAAI,CAACI,OAAO,CAAC,CAACE,KAAK,CAACC;IACrC;IAEOC,GAAGC,KAAa,EAAEC,QAAkC,EAAQ;QACjEV,OAAO,CAAC,IAAI,CAACI,OAAO,CAAC,CAACI,EAAE,CAACC,OAAOC;QAChC,OAAO,IAAI;IACb;IAEOC,KAAKF,KAAa,EAAEC,QAAkC,EAAQ;QACnEV,OAAO,CAAC,IAAI,CAACI,OAAO,CAAC,CAACO,IAAI,CAACF,OAAOC;QAClC,OAAO,IAAI;IACb;IA7BA,YAAmB,AAAON,OAA4B,CAAE;;aAA9BA,UAAAA;QACxBJ,OAAO,CAAC,IAAI,CAACI,OAAO,CAAC,CAACI,EAAE,CAAC,SAAS,CAACI;YACjC,IAAIA,IAAIC,IAAI,KAAK,SAAS;gBACxB;YACF;YACA,MAAMD;QACR;IACF;AAuBF;AAEA,OAAO,MAAME,SAAS,IAAIZ,OAAO,UAAU;AAC3C,OAAO,MAAMa,SAAS,IAAIb,OAAO,UAAU;AAE3C,OAAO,MAAMc,SAAS,CAACC,UAAyC,GAAGC;IACjE,MAAMC,UAAUrB,EAAEsB,QAAQ,CAACH,YAAYA,WAAWrB,cAAcqB,aAAaC;IAC7E,OAAOjB,OAAOkB;AAChB,EAAE;AAEF,OAAO,MAAME,QAAQ,CAACJ,UAAyC,GAAGC;IAChEJ,OAAOR,KAAK,CAACU,OAAOC,aAAaC;AACnC,EAAE;AAEF,OAAO,MAAMI,UAAU,CAACL,UAA0C,GAAGC;IACnE,IAAID,UAAUH,OAAOR,KAAK,CAACU,OAAOC,aAAaC;IAC/CJ,OAAOR,KAAK,CAAC;AACf,EAAE;AAEF,OAAO,MAAMiB,oBAAoB,CAACC,OAAeC;IAC/C1B,OAAO0B,QAAQC,MAAM,GAAG,GAAG;IAC3B,OAAO5B,EAAE6B,MAAM,CAACF,SAAS,CAACG,MAAQ/B,YAAYgC,GAAG,CAACD,KAAKJ;AACzD,EAAE"}
|
package/lib/services/promise.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Long lived references to Promises stress the garbage collector in JS.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Long lived references to Promises stress the garbage collector in JS.
|
|
3
|
+
*
|
|
4
|
+
* Instead of caching resolved Promises, we cache these little data
|
|
5
|
+
* objects instead which reference the resolution or rejection of the
|
|
6
|
+
* Promise, allowing the Promise object to be free'd.
|
|
5
7
|
*/ import { _ as _define_property } from "@swc/helpers/_/_define_property";
|
|
6
8
|
export class PromiseWrapper {
|
|
7
9
|
async unwrap() {
|
|
@@ -33,8 +35,9 @@ let _Symbol_toStringTag = Symbol.toStringTag;
|
|
|
33
35
|
/**
|
|
34
36
|
* A promise that can be resolved or rejected from outside its callback.
|
|
35
37
|
*
|
|
36
|
-
* This is typically used when you want to await a promise that is
|
|
37
|
-
* such as from an
|
|
38
|
+
* This is typically used when you want to await a promise that is
|
|
39
|
+
* resolved or rejected from outside the current scope, such as from an
|
|
40
|
+
* event handler.
|
|
38
41
|
*
|
|
39
42
|
* @example
|
|
40
43
|
* const signal = new PromiseSignal();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/promise.ts"],"sourcesContent":["/**\n * Long lived references to Promises stress the garbage collector in JS
|
|
1
|
+
{"version":3,"sources":["../../src/services/promise.ts"],"sourcesContent":["/**\n * Long lived references to Promises stress the garbage collector in JS.\n *\n * Instead of caching resolved Promises, we cache these little data\n * objects instead which reference the resolution or rejection of the\n * Promise, allowing the Promise 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\n * resolved or rejected from outside the current scope, such as from an\n * 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;;;;;;CAMC;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;IAiBYQ,sBAAAA,OAAOC,WAAW;AAf9B;;;;;;;;;;;;;CAaC,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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import fs from "fs-extra";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { config } from "./config.js";
|
|
4
|
+
import { swallowEnoent } from "./fs-utils.js";
|
|
5
|
+
import { createLogger } from "./log.js";
|
|
6
|
+
const log = createLogger("session");
|
|
7
|
+
export const readSession = ()=>{
|
|
8
|
+
log.debug("reading session from disk");
|
|
9
|
+
try {
|
|
10
|
+
return fs.readFileSync(path.join(config.configDir, "session.txt"), "utf-8");
|
|
11
|
+
} catch (error) {
|
|
12
|
+
swallowEnoent(error);
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
export const writeSession = (session)=>{
|
|
17
|
+
log.debug("writing session to disk", {
|
|
18
|
+
session: Boolean(session)
|
|
19
|
+
});
|
|
20
|
+
if (session) {
|
|
21
|
+
fs.outputFileSync(path.join(config.configDir, "session.txt"), session);
|
|
22
|
+
} else {
|
|
23
|
+
fs.removeSync(path.join(config.configDir, "session.txt"));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/session.ts"],"sourcesContent":["import fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { config } from \"./config.js\";\nimport { swallowEnoent } from \"./fs-utils.js\";\nimport { createLogger } from \"./log.js\";\n\nconst log = createLogger(\"session\");\n\nexport const readSession = (): string | undefined => {\n log.debug(\"reading session from disk\");\n\n try {\n return fs.readFileSync(path.join(config.configDir, \"session.txt\"), \"utf-8\");\n } catch (error) {\n swallowEnoent(error);\n return undefined;\n }\n};\n\nexport const writeSession = (session: string | undefined) => {\n log.debug(\"writing session to disk\", { session: Boolean(session) });\n\n if (session) {\n fs.outputFileSync(path.join(config.configDir, \"session.txt\"), session);\n } else {\n fs.removeSync(path.join(config.configDir, \"session.txt\"));\n }\n};\n"],"names":["fs","path","config","swallowEnoent","createLogger","log","readSession","debug","readFileSync","join","configDir","error","undefined","writeSession","session","Boolean","outputFileSync","removeSync"],"mappings":"AAAA,OAAOA,QAAQ,WAAW;AAC1B,OAAOC,UAAU,YAAY;AAC7B,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,aAAa,QAAQ,gBAAgB;AAC9C,SAASC,YAAY,QAAQ,WAAW;AAExC,MAAMC,MAAMD,aAAa;AAEzB,OAAO,MAAME,cAAc;IACzBD,IAAIE,KAAK,CAAC;IAEV,IAAI;QACF,OAAOP,GAAGQ,YAAY,CAACP,KAAKQ,IAAI,CAACP,OAAOQ,SAAS,EAAE,gBAAgB;IACrE,EAAE,OAAOC,OAAO;QACdR,cAAcQ;QACd,OAAOC;IACT;AACF,EAAE;AAEF,OAAO,MAAMC,eAAe,CAACC;IAC3BT,IAAIE,KAAK,CAAC,2BAA2B;QAAEO,SAASC,QAAQD;IAAS;IAEjE,IAAIA,SAAS;QACXd,GAAGgB,cAAc,CAACf,KAAKQ,IAAI,CAACP,OAAOQ,SAAS,EAAE,gBAAgBI;IAChE,OAAO;QACLd,GAAGiB,UAAU,CAAChB,KAAKQ,IAAI,CAACP,OAAOQ,SAAS,EAAE;IAC5C;AACF,EAAE"}
|
package/lib/services/sleep.js
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
import { timeoutMs } from "./timeout.js";
|
|
1
2
|
export function sleep(ms = 0) {
|
|
2
3
|
return new Promise((resolve)=>ms == 0 ? setImmediate(resolve) : setTimeout(resolve, ms));
|
|
3
4
|
}
|
|
4
|
-
export async function sleepUntil(fn, { interval = 0, timeout =
|
|
5
|
-
if (process.env["CI"]) {
|
|
6
|
-
// double the timeout in CI to account for slower machines
|
|
7
|
-
timeout *= 2;
|
|
8
|
-
}
|
|
5
|
+
export async function sleepUntil(fn, { interval = 0, timeout = timeoutMs("5s") } = {}) {
|
|
9
6
|
const start = isFinite(timeout) && Date.now();
|
|
10
7
|
// eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition
|
|
11
8
|
while(true){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/services/sleep.ts"],"sourcesContent":["
|
|
1
|
+
{"version":3,"sources":["../../src/services/sleep.ts"],"sourcesContent":["import { timeoutMs } from \"./timeout.js\";\n\nexport 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 = timeoutMs(\"5s\") } = {}): Promise<void> {\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":["timeoutMs","sleep","ms","Promise","resolve","setImmediate","setTimeout","sleepUntil","fn","interval","timeout","start","isFinite","Date","now","error","Error","captureStackTrace"],"mappings":"AAAA,SAASA,SAAS,QAAQ,eAAe;AAEzC,OAAO,SAASC,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,UAAUV,UAAU,KAAK,EAAE,GAAG,CAAC,CAAC;IAClG,MAAMW,QAAQC,SAASF,YAAYG,KAAKC,GAAG;IAE3C,8FAA8F;IAC9F,MAAO,KAAM;QACX,IAAIN,MAAM;QACV,MAAMP,MAAMQ;QAEZ,IAAIE,SAASE,KAAKC,GAAG,KAAKH,QAAQD,SAAS;YACzC,MAAMK,QAAQ,IAAIC,MAAM,CAAC,gBAAgB,EAAEN,QAAQ,aAAa,CAAC;YACjEM,MAAMC,iBAAiB,CAACF,OAAOR;YAC/B,MAAMQ;QACR;IACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/timeout.ts"],"sourcesContent":["import ms from \"ms\";\nimport process from \"node:process\";\n\nexport const timeoutMs = (duration: string) => {\n const milliseconds = ms(duration);\n return process.env[\"CI\"] ? milliseconds * 2 : milliseconds;\n};\n"],"names":["ms","process","timeoutMs","duration","milliseconds","env"],"mappings":"AAAA,OAAOA,QAAQ,KAAK;AACpB,OAAOC,aAAa,eAAe;AAEnC,OAAO,MAAMC,YAAY,CAACC;IACxB,MAAMC,eAAeJ,GAAGG;IACxB,OAAOF,QAAQI,GAAG,CAAC,KAAK,GAAGD,eAAe,IAAIA;AAChD,EAAE"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import inquirer from "inquirer";
|
|
2
|
+
import _ from "lodash";
|
|
3
|
+
import assert from "node:assert";
|
|
4
|
+
import z from "zod";
|
|
5
|
+
import { run as login } from "../commands/login.js";
|
|
6
|
+
import { config } from "./config.js";
|
|
7
|
+
import { setUser } from "./errors.js";
|
|
8
|
+
import { http, loadCookie, swallowUnauthorized } from "./http.js";
|
|
9
|
+
import { createLogger } from "./log.js";
|
|
10
|
+
const log = createLogger("user");
|
|
11
|
+
const User = z.object({
|
|
12
|
+
id: z.union([
|
|
13
|
+
z.string(),
|
|
14
|
+
z.number()
|
|
15
|
+
]).transform(Number),
|
|
16
|
+
name: z.string().nullish(),
|
|
17
|
+
email: z.string()
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* @returns The current user.
|
|
21
|
+
*/ export const getUser = async ()=>{
|
|
22
|
+
const cookie = loadCookie();
|
|
23
|
+
if (!cookie) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const json = await http({
|
|
28
|
+
url: `https://${config.domains.services}/auth/api/current-user`,
|
|
29
|
+
headers: {
|
|
30
|
+
cookie
|
|
31
|
+
},
|
|
32
|
+
responseType: "json",
|
|
33
|
+
resolveBodyOnly: true
|
|
34
|
+
});
|
|
35
|
+
const user = User.parse(json);
|
|
36
|
+
setUser(user);
|
|
37
|
+
log.info("loaded current user", {
|
|
38
|
+
user: _.pick(user, [
|
|
39
|
+
"id",
|
|
40
|
+
"name",
|
|
41
|
+
"email"
|
|
42
|
+
])
|
|
43
|
+
});
|
|
44
|
+
return user;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
swallowUnauthorized(error);
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
export const getUserOrLogin = async (message = "You must be logged in to use this command. Would you like to log in?")=>{
|
|
51
|
+
let user = await getUser();
|
|
52
|
+
if (user) {
|
|
53
|
+
return user;
|
|
54
|
+
}
|
|
55
|
+
log.info("prompting user to log in");
|
|
56
|
+
const { yes } = await inquirer.prompt({
|
|
57
|
+
type: "confirm",
|
|
58
|
+
name: "yes",
|
|
59
|
+
message
|
|
60
|
+
});
|
|
61
|
+
if (!yes) {
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
await login();
|
|
65
|
+
user = await getUser();
|
|
66
|
+
assert(user, "missing user after successful login");
|
|
67
|
+
return user;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=user.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/user.ts"],"sourcesContent":["import inquirer from \"inquirer\";\nimport _ from \"lodash\";\nimport assert from \"node:assert\";\nimport z from \"zod\";\nimport { run as login } from \"../commands/login.js\";\nimport { config } from \"./config.js\";\nimport { setUser } from \"./errors.js\";\nimport { http, loadCookie, swallowUnauthorized } from \"./http.js\";\nimport { createLogger } from \"./log.js\";\n\nconst log = createLogger(\"user\");\n\nconst User = z.object({\n id: z.union([z.string(), z.number()]).transform(Number),\n name: z.string().nullish(),\n email: z.string(),\n});\n\nexport type User = z.infer<typeof User>;\n\n/**\n * @returns The current user.\n */\nexport const getUser = async (): Promise<User | undefined> => {\n const cookie = loadCookie();\n if (!cookie) {\n return undefined;\n }\n\n try {\n const json = await http({\n url: `https://${config.domains.services}/auth/api/current-user`,\n headers: { cookie },\n responseType: \"json\",\n resolveBodyOnly: true,\n });\n\n const user = User.parse(json);\n setUser(user);\n log.info(\"loaded current user\", { user: _.pick(user, [\"id\", \"name\", \"email\"]) });\n\n return user;\n } catch (error) {\n swallowUnauthorized(error);\n return undefined;\n }\n};\n\nexport const getUserOrLogin = async (message = \"You must be logged in to use this command. Would you like to log in?\"): Promise<User> => {\n let user = await getUser();\n if (user) {\n return user;\n }\n\n log.info(\"prompting user to log in\");\n const { yes } = await inquirer.prompt<{ yes: boolean }>({\n type: \"confirm\",\n name: \"yes\",\n message,\n });\n\n if (!yes) {\n process.exit(0);\n }\n\n await login();\n\n user = await getUser();\n assert(user, \"missing user after successful login\");\n\n return user;\n};\n"],"names":["inquirer","_","assert","z","run","login","config","setUser","http","loadCookie","swallowUnauthorized","createLogger","log","User","object","id","union","string","number","transform","Number","name","nullish","email","getUser","cookie","undefined","json","url","domains","services","headers","responseType","resolveBodyOnly","user","parse","info","pick","error","getUserOrLogin","message","yes","prompt","type","process","exit"],"mappings":"AAAA,OAAOA,cAAc,WAAW;AAChC,OAAOC,OAAO,SAAS;AACvB,OAAOC,YAAY,cAAc;AACjC,OAAOC,OAAO,MAAM;AACpB,SAASC,OAAOC,KAAK,QAAQ,uBAAuB;AACpD,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,OAAO,QAAQ,cAAc;AACtC,SAASC,IAAI,EAAEC,UAAU,EAAEC,mBAAmB,QAAQ,YAAY;AAClE,SAASC,YAAY,QAAQ,WAAW;AAExC,MAAMC,MAAMD,aAAa;AAEzB,MAAME,OAAOV,EAAEW,MAAM,CAAC;IACpBC,IAAIZ,EAAEa,KAAK,CAAC;QAACb,EAAEc,MAAM;QAAId,EAAEe,MAAM;KAAG,EAAEC,SAAS,CAACC;IAChDC,MAAMlB,EAAEc,MAAM,GAAGK,OAAO;IACxBC,OAAOpB,EAAEc,MAAM;AACjB;AAIA;;CAEC,GACD,OAAO,MAAMO,UAAU;IACrB,MAAMC,SAAShB;IACf,IAAI,CAACgB,QAAQ;QACX,OAAOC;IACT;IAEA,IAAI;QACF,MAAMC,OAAO,MAAMnB,KAAK;YACtBoB,KAAK,CAAC,QAAQ,EAAEtB,OAAOuB,OAAO,CAACC,QAAQ,CAAC,sBAAsB,CAAC;YAC/DC,SAAS;gBAAEN;YAAO;YAClBO,cAAc;YACdC,iBAAiB;QACnB;QAEA,MAAMC,OAAOrB,KAAKsB,KAAK,CAACR;QACxBpB,QAAQ2B;QACRtB,IAAIwB,IAAI,CAAC,uBAAuB;YAAEF,MAAMjC,EAAEoC,IAAI,CAACH,MAAM;gBAAC;gBAAM;gBAAQ;aAAQ;QAAE;QAE9E,OAAOA;IACT,EAAE,OAAOI,OAAO;QACd5B,oBAAoB4B;QACpB,OAAOZ;IACT;AACF,EAAE;AAEF,OAAO,MAAMa,iBAAiB,OAAOC,UAAU,sEAAsE;IACnH,IAAIN,OAAO,MAAMV;IACjB,IAAIU,MAAM;QACR,OAAOA;IACT;IAEAtB,IAAIwB,IAAI,CAAC;IACT,MAAM,EAAEK,GAAG,EAAE,GAAG,MAAMzC,SAAS0C,MAAM,CAAmB;QACtDC,MAAM;QACNtB,MAAM;QACNmB;IACF;IAEA,IAAI,CAACC,KAAK;QACRG,QAAQC,IAAI,CAAC;IACf;IAEA,MAAMxC;IAEN6B,OAAO,MAAMV;IACbtB,OAAOgC,MAAM;IAEb,OAAOA;AACT,EAAE"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import boxen from "boxen";
|
|
2
|
+
import { isAfter } from "date-fns";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import ms from "ms";
|
|
5
|
+
import assert from "node:assert";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import semver from "semver";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { config } from "./config.js";
|
|
10
|
+
import { http } from "./http.js";
|
|
11
|
+
import { createLogger } from "./log.js";
|
|
12
|
+
import { println, sprint } from "./output.js";
|
|
13
|
+
const log = createLogger("version");
|
|
14
|
+
const UPDATE_CHECK_FREQUENCY = ms("12 hours");
|
|
15
|
+
export const getDistTags = async ()=>{
|
|
16
|
+
const json = await http({
|
|
17
|
+
method: "GET",
|
|
18
|
+
url: "https://registry.npmjs.org/ggt",
|
|
19
|
+
responseType: "json",
|
|
20
|
+
resolveBodyOnly: true,
|
|
21
|
+
timeout: {
|
|
22
|
+
request: ms("5s")
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const parsed = z.object({
|
|
26
|
+
name: z.literal("ggt"),
|
|
27
|
+
"dist-tags": z.object({
|
|
28
|
+
latest: z.string()
|
|
29
|
+
})
|
|
30
|
+
}).parse(json);
|
|
31
|
+
return parsed["dist-tags"];
|
|
32
|
+
};
|
|
33
|
+
export const shouldCheckForUpdate = async ()=>{
|
|
34
|
+
try {
|
|
35
|
+
const lastCheck = Number(await fs.readFile(path.join(config.cacheDir, "last-update-check"), "utf-8"));
|
|
36
|
+
assert(!Number.isNaN(lastCheck));
|
|
37
|
+
return isAfter(Date.now(), lastCheck + UPDATE_CHECK_FREQUENCY);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
export const warnIfUpdateAvailable = async ()=>{
|
|
43
|
+
try {
|
|
44
|
+
const shouldCheck = await shouldCheckForUpdate();
|
|
45
|
+
if (!shouldCheck) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
await fs.outputFile(path.join(config.cacheDir, "last-update-check"), String(Date.now()));
|
|
49
|
+
const tags = await getDistTags();
|
|
50
|
+
if (semver.lt(config.version, tags.latest)) {
|
|
51
|
+
log.info("update available", {
|
|
52
|
+
current: config.version,
|
|
53
|
+
latest: tags.latest
|
|
54
|
+
});
|
|
55
|
+
println(boxen(sprint`
|
|
56
|
+
Update available! {red ${config.version}} -> {green ${tags.latest}}.
|
|
57
|
+
Changelog: https://github.com/gadget-inc/ggt/releases/tag/v${tags.latest}
|
|
58
|
+
Run "npm install -g ${config.name}" to update.
|
|
59
|
+
`, {
|
|
60
|
+
padding: 1,
|
|
61
|
+
borderStyle: "round",
|
|
62
|
+
textAlignment: "center"
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
log.error("failed to check for updates", {
|
|
67
|
+
error
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/services/version.ts"],"sourcesContent":["import boxen from \"boxen\";\nimport { isAfter } from \"date-fns\";\nimport fs from \"fs-extra\";\nimport ms from \"ms\";\nimport assert from \"node:assert\";\nimport path from \"node:path\";\nimport semver from \"semver\";\nimport { z } from \"zod\";\nimport { config } from \"./config.js\";\nimport { http } from \"./http.js\";\nimport { createLogger } from \"./log.js\";\nimport { println, sprint } from \"./output.js\";\n\nconst log = createLogger(\"version\");\n\nconst UPDATE_CHECK_FREQUENCY = ms(\"12 hours\");\n\nexport const getDistTags = async () => {\n const json = await http({\n method: \"GET\",\n url: \"https://registry.npmjs.org/ggt\",\n responseType: \"json\",\n resolveBodyOnly: true,\n timeout: {\n request: ms(\"5s\"),\n },\n });\n\n const parsed = z\n .object({\n name: z.literal(\"ggt\"),\n \"dist-tags\": z.object({\n latest: z.string(),\n }),\n })\n .parse(json);\n\n return parsed[\"dist-tags\"];\n};\n\nexport const shouldCheckForUpdate = async () => {\n try {\n const lastCheck = Number(await fs.readFile(path.join(config.cacheDir, \"last-update-check\"), \"utf-8\"));\n assert(!Number.isNaN(lastCheck));\n return isAfter(Date.now(), lastCheck + UPDATE_CHECK_FREQUENCY);\n } catch (error) {\n return true;\n }\n};\n\nexport const warnIfUpdateAvailable = async () => {\n try {\n const shouldCheck = await shouldCheckForUpdate();\n if (!shouldCheck) {\n return;\n }\n\n await fs.outputFile(path.join(config.cacheDir, \"last-update-check\"), String(Date.now()));\n\n const tags = await getDistTags();\n\n if (semver.lt(config.version, tags.latest)) {\n log.info(\"update available\", { current: config.version, latest: tags.latest });\n println(\n boxen(\n sprint`\n Update available! {red ${config.version}} -> {green ${tags.latest}}.\n Changelog: https://github.com/gadget-inc/ggt/releases/tag/v${tags.latest}\n Run \"npm install -g ${config.name}\" to update.\n `,\n {\n padding: 1,\n borderStyle: \"round\",\n textAlignment: \"center\",\n },\n ),\n );\n }\n } catch (error) {\n log.error(\"failed to check for updates\", { error });\n }\n};\n"],"names":["boxen","isAfter","fs","ms","assert","path","semver","z","config","http","createLogger","println","sprint","log","UPDATE_CHECK_FREQUENCY","getDistTags","json","method","url","responseType","resolveBodyOnly","timeout","request","parsed","object","name","literal","latest","string","parse","shouldCheckForUpdate","lastCheck","Number","readFile","join","cacheDir","isNaN","Date","now","error","warnIfUpdateAvailable","shouldCheck","outputFile","String","tags","lt","version","info","current","padding","borderStyle","textAlignment"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAC1B,SAASC,OAAO,QAAQ,WAAW;AACnC,OAAOC,QAAQ,WAAW;AAC1B,OAAOC,QAAQ,KAAK;AACpB,OAAOC,YAAY,cAAc;AACjC,OAAOC,UAAU,YAAY;AAC7B,OAAOC,YAAY,SAAS;AAC5B,SAASC,CAAC,QAAQ,MAAM;AACxB,SAASC,MAAM,QAAQ,cAAc;AACrC,SAASC,IAAI,QAAQ,YAAY;AACjC,SAASC,YAAY,QAAQ,WAAW;AACxC,SAASC,OAAO,EAAEC,MAAM,QAAQ,cAAc;AAE9C,MAAMC,MAAMH,aAAa;AAEzB,MAAMI,yBAAyBX,GAAG;AAElC,OAAO,MAAMY,cAAc;IACzB,MAAMC,OAAO,MAAMP,KAAK;QACtBQ,QAAQ;QACRC,KAAK;QACLC,cAAc;QACdC,iBAAiB;QACjBC,SAAS;YACPC,SAASnB,GAAG;QACd;IACF;IAEA,MAAMoB,SAAShB,EACZiB,MAAM,CAAC;QACNC,MAAMlB,EAAEmB,OAAO,CAAC;QAChB,aAAanB,EAAEiB,MAAM,CAAC;YACpBG,QAAQpB,EAAEqB,MAAM;QAClB;IACF,GACCC,KAAK,CAACb;IAET,OAAOO,MAAM,CAAC,YAAY;AAC5B,EAAE;AAEF,OAAO,MAAMO,uBAAuB;IAClC,IAAI;QACF,MAAMC,YAAYC,OAAO,MAAM9B,GAAG+B,QAAQ,CAAC5B,KAAK6B,IAAI,CAAC1B,OAAO2B,QAAQ,EAAE,sBAAsB;QAC5F/B,OAAO,CAAC4B,OAAOI,KAAK,CAACL;QACrB,OAAO9B,QAAQoC,KAAKC,GAAG,IAAIP,YAAYjB;IACzC,EAAE,OAAOyB,OAAO;QACd,OAAO;IACT;AACF,EAAE;AAEF,OAAO,MAAMC,wBAAwB;IACnC,IAAI;QACF,MAAMC,cAAc,MAAMX;QAC1B,IAAI,CAACW,aAAa;YAChB;QACF;QAEA,MAAMvC,GAAGwC,UAAU,CAACrC,KAAK6B,IAAI,CAAC1B,OAAO2B,QAAQ,EAAE,sBAAsBQ,OAAON,KAAKC,GAAG;QAEpF,MAAMM,OAAO,MAAM7B;QAEnB,IAAIT,OAAOuC,EAAE,CAACrC,OAAOsC,OAAO,EAAEF,KAAKjB,MAAM,GAAG;YAC1Cd,IAAIkC,IAAI,CAAC,oBAAoB;gBAAEC,SAASxC,OAAOsC,OAAO;gBAAEnB,QAAQiB,KAAKjB,MAAM;YAAC;YAC5EhB,QACEX,MACEY,MAAM,CAAC;mCACkB,EAAEJ,OAAOsC,OAAO,CAAC,YAAY,EAAEF,KAAKjB,MAAM,CAAC;uEACP,EAAEiB,KAAKjB,MAAM,CAAC;gCACrD,EAAEnB,OAAOiB,IAAI,CAAC;UACpC,CAAC,EACD;gBACEwB,SAAS;gBACTC,aAAa;gBACbC,eAAe;YACjB;QAGN;IACF,EAAE,OAAOZ,OAAO;QACd1B,IAAI0B,KAAK,CAAC,+BAA+B;YAAEA;QAAM;IACnD;AACF,EAAE"}
|