@bitvea/feedback-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +24 -0
- package/dist/bin.js.map +1 -0
- package/dist/commands/init.d.ts +97 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +273 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/register.d.ts +84 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +125 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/install/env.d.ts +2 -0
- package/dist/install/env.d.ts.map +1 -0
- package/dist/install/env.js +19 -0
- package/dist/install/env.js.map +1 -0
- package/dist/install/fs.d.ts +16 -0
- package/dist/install/fs.d.ts.map +1 -0
- package/dist/install/fs.js +33 -0
- package/dist/install/fs.js.map +1 -0
- package/dist/install/strategies.d.ts +109 -0
- package/dist/install/strategies.d.ts.map +1 -0
- package/dist/install/strategies.js +208 -0
- package/dist/install/strategies.js.map +1 -0
- package/dist/registration.d.ts +43 -0
- package/dist/registration.d.ts.map +1 -0
- package/dist/registration.js +84 -0
- package/dist/registration.js.map +1 -0
- package/dist/run.d.ts +14 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +293 -0
- package/dist/run.js.map +1 -0
- package/dist/toolbarVersion.d.ts +36 -0
- package/dist/toolbarVersion.d.ts.map +1 -0
- package/dist/toolbarVersion.js +106 -0
- package/dist/toolbarVersion.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Talking to the registration endpoint, and nothing else.
|
|
2
|
+
//
|
|
3
|
+
// The rule this file exists to keep is that the CLI does not know what a valid
|
|
4
|
+
// project URL is. Registration's contract - normalise the URL, key on it, be
|
|
5
|
+
// idempotent, lose the race gracefully - lives once in
|
|
6
|
+
// `packages/api/src/services/projects.ts`, behind
|
|
7
|
+
// `POST /api/v1/projects/register`. A second opinion here would be a second
|
|
8
|
+
// implementation, and the failure mode of two implementations disagreeing is a
|
|
9
|
+
// duplicate project: two rows for one deployment, each holding half its
|
|
10
|
+
// feedback, with no way to merge them afterwards.
|
|
11
|
+
//
|
|
12
|
+
// So this module posts what it was given and translates the answer. It trims
|
|
13
|
+
// no slashes, lowercases no hosts, and rejects nothing.
|
|
14
|
+
/**
|
|
15
|
+
* A failure the person running the command can act on, as opposed to a bug.
|
|
16
|
+
* The CLI prints `message` and exits; nothing else is expected to catch it.
|
|
17
|
+
*/
|
|
18
|
+
export class RegistrationError extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "RegistrationError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** The real transport. Injected everywhere else so tests stay off the network. */
|
|
25
|
+
export const postRegistration = async (input) => {
|
|
26
|
+
let res;
|
|
27
|
+
try {
|
|
28
|
+
res = await fetch(`${input.apiUrl}/api/v1/projects/register`, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: {
|
|
31
|
+
"content-type": "application/json",
|
|
32
|
+
authorization: `Bearer ${input.apiKey}`,
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify({
|
|
35
|
+
url: input.projectUrl,
|
|
36
|
+
repoOwner: input.repoOwner,
|
|
37
|
+
repoSlug: input.repoSlug,
|
|
38
|
+
packageName: input.packageName,
|
|
39
|
+
origins: input.origins,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
// DNS, TLS, a wrong host, no network in the build container. Worth naming
|
|
45
|
+
// the URL: the commonest cause is an --api-url pointing at nothing.
|
|
46
|
+
throw new RegistrationError(`could not reach ${input.apiUrl} (${String(err)}). Check --api-url.`);
|
|
47
|
+
}
|
|
48
|
+
if (res.status === 401) {
|
|
49
|
+
// The commonest real failure, and the one a bare status code explains
|
|
50
|
+
// worst. Absent, mistyped and revoked all answer 401 on purpose - the
|
|
51
|
+
// route refuses to be an oracle for which - so say all three.
|
|
52
|
+
throw new RegistrationError("the deploy key was refused (401): it is missing, wrong or revoked. " +
|
|
53
|
+
"Create one in the dashboard under Settings > Deploy keys, then pass it as " +
|
|
54
|
+
"--api-key or set FEEDBACK_TOOLBAR_API_KEY.");
|
|
55
|
+
}
|
|
56
|
+
if (!res.ok) {
|
|
57
|
+
// The backend's own error code, verbatim. `invalid_project_url` is the one
|
|
58
|
+
// that matters, and paraphrasing it here would be the beginning of a
|
|
59
|
+
// second set of validation rules.
|
|
60
|
+
const code = await res
|
|
61
|
+
.json()
|
|
62
|
+
.then((body) => body !== null && typeof body === "object" && "error" in body
|
|
63
|
+
? String(body.error)
|
|
64
|
+
: undefined)
|
|
65
|
+
.catch(() => undefined);
|
|
66
|
+
// The one refusal a build operator can act on, and the one whose bare code
|
|
67
|
+
// explains nothing. A deploy key belongs to exactly one agency and may
|
|
68
|
+
// register URLs into that agency alone, so this fires when the URL is
|
|
69
|
+
// already registered somewhere the key cannot write. What it deliberately
|
|
70
|
+
// does NOT say is where: the route answers one opaque code precisely so it
|
|
71
|
+
// cannot be used to enumerate which deployment URLs exist in an install and
|
|
72
|
+
// which customer runs them, and repeating a guess here would give away by
|
|
73
|
+
// paraphrase what the status code refuses to give away directly.
|
|
74
|
+
if (code === "project_url_unavailable") {
|
|
75
|
+
throw new RegistrationError(`${input.projectUrl} cannot be registered with this deploy key (409). ` +
|
|
76
|
+
"Either it is already registered outside this key's agency, or the key " +
|
|
77
|
+
"belongs to a different agency than the one that should own it. Check " +
|
|
78
|
+
"which key the build is using under Settings > Deploy keys.");
|
|
79
|
+
}
|
|
80
|
+
throw new RegistrationError(`registration failed with ${res.status}${code ? ` (${code})` : ""}.`);
|
|
81
|
+
}
|
|
82
|
+
return (await res.json());
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../src/registration.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,uDAAuD;AACvD,kDAAkD;AAClD,4EAA4E;AAC5E,+EAA+E;AAC/E,wEAAwE;AACxE,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,wDAAwD;AAmCxD;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAA6B,KAAK,EAAE,KAAK,EAAE,EAAE;IACxE,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,2BAA2B,EAAE;YAC5D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,KAAK,CAAC,MAAM,EAAE;aACxC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,GAAG,EAAE,KAAK,CAAC,UAAU;gBACrB,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,0EAA0E;QAC1E,oEAAoE;QACpE,MAAM,IAAI,iBAAiB,CACzB,mBAAmB,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,GAAG,CAAC,qBAAqB,CACrE,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,sEAAsE;QACtE,sEAAsE;QACtE,8DAA8D;QAC9D,MAAM,IAAI,iBAAiB,CACzB,qEAAqE;YACnE,4EAA4E;YAC5E,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,2EAA2E;QAC3E,qEAAqE;QACrE,kCAAkC;QAClC,MAAM,IAAI,GAAG,MAAM,GAAG;aACnB,IAAI,EAAE;aACN,IAAI,CAAC,CAAC,IAAa,EAAE,EAAE,CACtB,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI;YAC1D,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;YACpB,CAAC,CAAC,SAAS,CACd;aACA,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAE1B,2EAA2E;QAC3E,uEAAuE;QACvE,sEAAsE;QACtE,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,0EAA0E;QAC1E,iEAAiE;QACjE,IAAI,IAAI,KAAK,yBAAyB,EAAE,CAAC;YACvC,MAAM,IAAI,iBAAiB,CACzB,GAAG,KAAK,CAAC,UAAU,oDAAoD;gBACrE,wEAAwE;gBACxE,uEAAuE;gBACvE,4DAA4D,CAC/D,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,iBAAiB,CACzB,4BAA4B,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CACrE,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;AACvD,CAAC,CAAC"}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface RunOptions {
|
|
2
|
+
/**
|
|
3
|
+
* The version of `@bitvea/feedback-toolbar` the caller was launched
|
|
4
|
+
* through, when there was one.
|
|
5
|
+
*
|
|
6
|
+
* Only `@bitvea/feedback-toolbar`'s own bin passes this, from the
|
|
7
|
+
* `package.json` sitting next to it, and it is the third rung of the ladder
|
|
8
|
+
* in ./toolbarVersion.ts - the one every `npx @bitvea/feedback-toolbar init`
|
|
9
|
+
* takes. Absent when this CLI was run directly.
|
|
10
|
+
*/
|
|
11
|
+
toolbarVersion?: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function run(argv: string[], options?: RunOptions): Promise<number>;
|
|
14
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,UAAU;IACzB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AA2PD,wBAAsB,GAAG,CACvB,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE,UAAe,GACvB,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// The command table, as a function: argv in, exit code out.
|
|
2
|
+
//
|
|
3
|
+
// THIS PACKAGE HAS ZERO RUNTIME DEPENDENCIES, and its publishable check fails
|
|
4
|
+
// if `dependencies` is ever non-empty. That is not asceticism. The whole
|
|
5
|
+
// reason the installer was hard to grow inside the toolbar is that every
|
|
6
|
+
// argument parser, every colour library, every prompt library it might want
|
|
7
|
+
// would land in a customer's browser app - and after the split it still would,
|
|
8
|
+
// because `@bitvea/feedback-toolbar` depends on this package, so this package
|
|
9
|
+
// is installed transitively into every app that installs the toolbar. Moving
|
|
10
|
+
// the files without taking the dependency budget with them would buy a package
|
|
11
|
+
// boundary and nothing else. Node's standard library covers what this CLI
|
|
12
|
+
// needs: `node:util`'s `parseArgs` and `styleText` when the hand-rolled flag
|
|
13
|
+
// reader below stops being enough, `node:readline` if a prompt is ever wanted.
|
|
14
|
+
//
|
|
15
|
+
// `run` is exported from the package root rather than being reachable only as
|
|
16
|
+
// a bin, and that is what lets `@bitvea/feedback-toolbar`'s own `bin` be an
|
|
17
|
+
// in-process call instead of a subprocess: same argv, same stdout, same exit
|
|
18
|
+
// codes, because it is the same function in the same process.
|
|
19
|
+
//
|
|
20
|
+
// Everything of substance is in commands/init.ts and commands/register.ts,
|
|
21
|
+
// behind an InstallerFs and an injected transport respectively. This file is
|
|
22
|
+
// the part that touches the real disk and the real network, parses argv, and
|
|
23
|
+
// turns a thrown error into an exit code and a message a developer can act on.
|
|
24
|
+
//
|
|
25
|
+
// `register` is a first-class command rather than a step inside `init`,
|
|
26
|
+
// because two install paths need it on its own: a Next.js app runs it from its
|
|
27
|
+
// build (init wires it in as `prebuild`), and a script-tag install has no
|
|
28
|
+
// build at all and runs it once from a terminal or CI.
|
|
29
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
30
|
+
import { dirname, join } from "node:path";
|
|
31
|
+
import { mkdirSync } from "node:fs";
|
|
32
|
+
import { fileURLToPath } from "node:url";
|
|
33
|
+
import { init } from "./commands/init.js";
|
|
34
|
+
import { API_KEY_ENV_VAR, register } from "./commands/register.js";
|
|
35
|
+
import { RegistrationError, postRegistration } from "./registration.js";
|
|
36
|
+
import { InjectionError, PACKAGE_NAME } from "./install/strategies.js";
|
|
37
|
+
import { ToolbarVersionError, resolveToolbarRange, } from "./toolbarVersion.js";
|
|
38
|
+
const realFs = {
|
|
39
|
+
exists: (path) => existsSync(path),
|
|
40
|
+
readFile: (path) => readFileSync(path, "utf8"),
|
|
41
|
+
writeFile: (path, contents) => {
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
43
|
+
writeFileSync(path, contents, "utf8");
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
// The usage text names `feedback-toolbar`, not this package, and that is
|
|
47
|
+
// deliberate rather than stale. `npx @bitvea/feedback-toolbar init` is printed
|
|
48
|
+
// on the product's homepage, in two dashboard panels and in a `prebuild` line
|
|
49
|
+
// committed in repositories nobody here controls; this package's name is an
|
|
50
|
+
// implementation detail that nobody should have to learn. The text is also
|
|
51
|
+
// read by both packages' publishable checks, which compare this stdout byte
|
|
52
|
+
// for byte across the launcher and this bin - so it says the same thing
|
|
53
|
+
// whichever door a person came in through.
|
|
54
|
+
const USAGE = `feedback-toolbar - BitVea Feedback installer
|
|
55
|
+
|
|
56
|
+
init Install the toolbar into the app in the current directory.
|
|
57
|
+
register Make sure this deployment exists as a project in the dashboard.
|
|
58
|
+
|
|
59
|
+
npx @bitvea/feedback-toolbar init --api-url https://feedback.bitvea.com
|
|
60
|
+
npx @bitvea/feedback-toolbar register --project-url https://myapp.com --api-key <key>
|
|
61
|
+
|
|
62
|
+
Flags
|
|
63
|
+
|
|
64
|
+
--api-url <url> BitVea Feedback backend. Falls back to
|
|
65
|
+
NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL.
|
|
66
|
+
--project-url <url> The deployment to register. Falls back to
|
|
67
|
+
VERCEL_PROJECT_PRODUCTION_URL, then VERCEL_URL.
|
|
68
|
+
--api-key <key> Deploy key. Falls back to FEEDBACK_TOOLBAR_API_KEY,
|
|
69
|
+
which is the better place for it: a key on a command
|
|
70
|
+
line ends up in shell history and CI logs. It is never
|
|
71
|
+
written to disk and never reaches a browser.
|
|
72
|
+
--root <path> The app to work on. Defaults to the current directory.
|
|
73
|
+
--environment <name> init, script-tag installs only: what the injected tag
|
|
74
|
+
declares. Defaults to "preview", which is what makes it
|
|
75
|
+
mount; pass "production" to leave the tag inert.
|
|
76
|
+
--toolbar-version <r> init only: the range to write into package.json for
|
|
77
|
+
@bitvea/feedback-toolbar. Defaults to the version this
|
|
78
|
+
command was launched through, then to the latest on the
|
|
79
|
+
registry. Nothing is guessed: if neither answers, the
|
|
80
|
+
install refuses rather than pin a number nobody chose.
|
|
81
|
+
--optional register only: report a failure and exit 0. This is
|
|
82
|
+
what the prebuild hook uses, so a registration problem
|
|
83
|
+
can never fail somebody's deploy.
|
|
84
|
+
--version Print versions. Two packages are in play since the
|
|
85
|
+
installer moved out of the toolbar, so this prints both
|
|
86
|
+
when it knows both.`;
|
|
87
|
+
function readFlag(argv, name) {
|
|
88
|
+
const withEquals = argv.find((a) => a.startsWith(`--${name}=`));
|
|
89
|
+
if (withEquals)
|
|
90
|
+
return withEquals.slice(name.length + 3);
|
|
91
|
+
const index = argv.indexOf(`--${name}`);
|
|
92
|
+
return index >= 0 ? argv[index + 1] : undefined;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The host app's package name, which the backend uses to name a project when
|
|
96
|
+
* there is no git metadata to name it from. Best effort by design: a missing
|
|
97
|
+
* or unreadable package.json is not a reason to refuse to register.
|
|
98
|
+
*/
|
|
99
|
+
function readPackageName(root) {
|
|
100
|
+
const path = join(root, "package.json");
|
|
101
|
+
if (!existsSync(path))
|
|
102
|
+
return undefined;
|
|
103
|
+
try {
|
|
104
|
+
const pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
105
|
+
return pkg.name;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* This package's own version, read off its own manifest.
|
|
113
|
+
*
|
|
114
|
+
* `../package.json` from this module resolves to the package root from both
|
|
115
|
+
* `src/` and `dist/`, which is why nothing here has to be stamped at build
|
|
116
|
+
* time. Unknown rather than fatal: `--version` failing to read a manifest is
|
|
117
|
+
* not a reason to fail the process it was asked to describe.
|
|
118
|
+
*/
|
|
119
|
+
function cliVersion() {
|
|
120
|
+
try {
|
|
121
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
122
|
+
const pkg = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8"));
|
|
123
|
+
return pkg.version ?? "unknown";
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return "unknown";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function runInit(argv, options) {
|
|
130
|
+
const apiUrl = readFlag(argv, "api-url") ??
|
|
131
|
+
process.env.NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL;
|
|
132
|
+
if (!apiUrl) {
|
|
133
|
+
console.error("missing --api-url. Pass the BitVea Feedback backend URL, e.g.\n" +
|
|
134
|
+
" npx @bitvea/feedback-toolbar init --api-url https://feedback.bitvea.com");
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
// Resolved lazily, and reported once: only the npm route writes a
|
|
138
|
+
// dependency, and a script-tag install on a machine with no registry must
|
|
139
|
+
// not be refused for a number it will never use.
|
|
140
|
+
let resolved;
|
|
141
|
+
try {
|
|
142
|
+
const result = await init(realFs, {
|
|
143
|
+
root: readFlag(argv, "root") ?? process.cwd(),
|
|
144
|
+
apiUrl,
|
|
145
|
+
// Only needed to register the project from this machine, which is the
|
|
146
|
+
// exception rather than the rule: the prebuild step init writes is what
|
|
147
|
+
// normally registers, from a build environment that has the key.
|
|
148
|
+
apiKey: readFlag(argv, "api-key") ?? process.env.FEEDBACK_TOOLBAR_API_KEY,
|
|
149
|
+
projectUrl: readFlag(argv, "project-url"),
|
|
150
|
+
environment: readFlag(argv, "environment"),
|
|
151
|
+
resolveToolbarRange: async () => {
|
|
152
|
+
resolved = await resolveToolbarRange({
|
|
153
|
+
flag: readFlag(argv, "toolbar-version"),
|
|
154
|
+
launcherVersion: options.toolbarVersion,
|
|
155
|
+
env: process.env,
|
|
156
|
+
});
|
|
157
|
+
return resolved.range;
|
|
158
|
+
},
|
|
159
|
+
registerProject: postRegistration,
|
|
160
|
+
env: process.env,
|
|
161
|
+
});
|
|
162
|
+
if (result.alreadyInstalled) {
|
|
163
|
+
console.log("Already installed. Nothing to change.");
|
|
164
|
+
for (const note of result.notes)
|
|
165
|
+
console.log(` - ${note}`);
|
|
166
|
+
return 0;
|
|
167
|
+
}
|
|
168
|
+
console.log(`Installed via the ${result.strategy} strategy:`);
|
|
169
|
+
for (const change of result.changes)
|
|
170
|
+
console.log(` - ${change}`);
|
|
171
|
+
for (const note of result.notes)
|
|
172
|
+
console.log(` - ${note}`);
|
|
173
|
+
const provenance = resolved && describeRange(resolved);
|
|
174
|
+
if (provenance)
|
|
175
|
+
console.log(` - ${provenance}`);
|
|
176
|
+
// What is actually left to do, named. The previous sign-off ("redeploy to
|
|
177
|
+
// finish") was wrong in a way that cost a whole issue: the deploy key is
|
|
178
|
+
// a secret, it can only come from the build environment, and without it
|
|
179
|
+
// the prebuild step registers nothing and no project ever appears.
|
|
180
|
+
console.log(result.delivery === "script-tag"
|
|
181
|
+
? "\nDeploy the page to finish. Nothing to install.\n" +
|
|
182
|
+
"Then register the project once, from a terminal or CI:\n" +
|
|
183
|
+
` npx ${PACKAGE_NAME} register --api-url ${apiUrl} --project-url <url> --api-key <key>`
|
|
184
|
+
: "\nTwo things left:\n" +
|
|
185
|
+
" 1. install dependencies, commit, and deploy\n" +
|
|
186
|
+
` 2. set ${API_KEY_ENV_VAR} in your hosting provider's environment variables\n` +
|
|
187
|
+
" (dashboard > Settings > Deploy keys)\n" +
|
|
188
|
+
"The build then registers this deployment itself, on every deploy. The backend\n" +
|
|
189
|
+
"URL needs no configuration: it is committed in package.json and in your layout.");
|
|
190
|
+
return 0;
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
if (err instanceof InjectionError || err instanceof ToolbarVersionError) {
|
|
194
|
+
console.error(`Could not install: ${err.message}`);
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Where that range came from, when the person running this did not choose it.
|
|
202
|
+
*
|
|
203
|
+
* Silent for `--toolbar-version` and `FEEDBACK_TOOLBAR_VERSION`, because
|
|
204
|
+
* telling somebody what they just typed is noise. The other two are worth a
|
|
205
|
+
* line: the number is about to be committed in their repository, and which of
|
|
206
|
+
* the two answered is the difference between "the toolbar you just downloaded"
|
|
207
|
+
* and "whatever npm currently calls latest".
|
|
208
|
+
*/
|
|
209
|
+
function describeRange(resolved) {
|
|
210
|
+
switch (resolved.source) {
|
|
211
|
+
case "launcher":
|
|
212
|
+
return "that range is the toolbar this command was launched through";
|
|
213
|
+
case "registry":
|
|
214
|
+
return "that range is the latest published version";
|
|
215
|
+
default:
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function runRegister(argv) {
|
|
220
|
+
const root = readFlag(argv, "root") ?? process.cwd();
|
|
221
|
+
// See USAGE: the prebuild hook passes this so that a registration problem
|
|
222
|
+
// is reported and forgiven rather than failing the host app's deploy.
|
|
223
|
+
const optional = argv.includes("--optional");
|
|
224
|
+
try {
|
|
225
|
+
const outcome = await register({
|
|
226
|
+
apiUrl: readFlag(argv, "api-url"),
|
|
227
|
+
apiKey: readFlag(argv, "api-key"),
|
|
228
|
+
projectUrl: readFlag(argv, "project-url"),
|
|
229
|
+
packageName: readPackageName(root),
|
|
230
|
+
env: process.env,
|
|
231
|
+
registerProject: postRegistration,
|
|
232
|
+
});
|
|
233
|
+
// Two distinct successes, said differently on purpose: somebody running
|
|
234
|
+
// this by hand needs to know whether they just created a project, and
|
|
235
|
+
// somebody reading a build log needs to see that a repeat run is normal.
|
|
236
|
+
if (outcome.created) {
|
|
237
|
+
const named = outcome.projectName ? ` as "${outcome.projectName}"` : "";
|
|
238
|
+
console.log(`Registered ${outcome.projectUrl}${named}. It is in the dashboard now.`);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
console.log(`${outcome.projectUrl} is already registered. Nothing to do.`);
|
|
242
|
+
}
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
if (err instanceof RegistrationError) {
|
|
247
|
+
if (optional) {
|
|
248
|
+
console.warn(`feedback-toolbar register: skipped - ${err.message}`);
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
console.error(`Could not register: ${err.message}`);
|
|
252
|
+
return 1;
|
|
253
|
+
}
|
|
254
|
+
throw err;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Both numbers, because after the split there are two.
|
|
259
|
+
*
|
|
260
|
+
* The installer and the component it installs now ship as separate packages on
|
|
261
|
+
* separate version lines, so "which halves are in play" stopped being
|
|
262
|
+
* answerable from the outside - and it is the first question of every support
|
|
263
|
+
* conversation about an install. No network: this reports what is on this
|
|
264
|
+
* machine, not what could be.
|
|
265
|
+
*/
|
|
266
|
+
function runVersion(options) {
|
|
267
|
+
console.log(`@bitvea/feedback-cli ${cliVersion()}`);
|
|
268
|
+
if (options.toolbarVersion) {
|
|
269
|
+
console.log(`${PACKAGE_NAME} ${options.toolbarVersion}`);
|
|
270
|
+
}
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
export async function run(argv, options = {}) {
|
|
274
|
+
const command = argv[0];
|
|
275
|
+
switch (command) {
|
|
276
|
+
case "init":
|
|
277
|
+
return runInit(argv, options);
|
|
278
|
+
case "register":
|
|
279
|
+
return runRegister(argv);
|
|
280
|
+
case "help":
|
|
281
|
+
case "--help":
|
|
282
|
+
case "-h":
|
|
283
|
+
console.log(USAGE);
|
|
284
|
+
return 0;
|
|
285
|
+
case "--version":
|
|
286
|
+
case "-v":
|
|
287
|
+
return runVersion(options);
|
|
288
|
+
default:
|
|
289
|
+
console.error(USAGE);
|
|
290
|
+
return 1;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Where the answer came from, so `--version` and `init` can say. */
|
|
2
|
+
export type ToolbarRangeSource = "flag" | "env" | "launcher" | "registry";
|
|
3
|
+
export declare const TOOLBAR_VERSION_ENV_VAR = "FEEDBACK_TOOLBAR_VERSION";
|
|
4
|
+
export interface ToolbarRangeInput {
|
|
5
|
+
/** `--toolbar-version`. */
|
|
6
|
+
flag?: string | undefined;
|
|
7
|
+
/** What the launcher in `@bitvea/feedback-toolbar` read off its own disk. */
|
|
8
|
+
launcherVersion?: string | undefined;
|
|
9
|
+
env: Record<string, string | undefined>;
|
|
10
|
+
/** Injected so tests stay off the network. */
|
|
11
|
+
fetchLatestVersion?: () => Promise<string | undefined>;
|
|
12
|
+
}
|
|
13
|
+
export interface ResolvedToolbarRange {
|
|
14
|
+
/** The range as it will be written, e.g. `^1.1.2`. */
|
|
15
|
+
range: string;
|
|
16
|
+
source: ToolbarRangeSource;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A refusal the person running the command can act on, as opposed to a bug.
|
|
20
|
+
* Carries the remedy, because the remedy is a flag they can type immediately.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ToolbarVersionError extends Error {
|
|
23
|
+
constructor(message: string);
|
|
24
|
+
}
|
|
25
|
+
export declare function resolveToolbarRange(input: ToolbarRangeInput): Promise<ResolvedToolbarRange>;
|
|
26
|
+
/**
|
|
27
|
+
* A bare `x.y.z` becomes `^x.y.z`; anything that already reads as a range is
|
|
28
|
+
* written through untouched.
|
|
29
|
+
*
|
|
30
|
+
* Untouched rather than normalised, because the shapes a person reaches for
|
|
31
|
+
* here - an exact pin, a `~`, a dist-tag like `next` - are all things they
|
|
32
|
+
* mean, and quietly turning a pin into a caret would be this command
|
|
33
|
+
* overruling the one decision it was explicitly told.
|
|
34
|
+
*/
|
|
35
|
+
export declare function asRange(value: string): string;
|
|
36
|
+
//# sourceMappingURL=toolbarVersion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbarVersion.d.ts","sourceRoot":"","sources":["../src/toolbarVersion.ts"],"names":[],"mappings":"AA0CA,qEAAqE;AACrE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,UAAU,CAAC;AAE1E,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CACxD;AAED,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,oBAAoB,CAAC,CAoB/B;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG7C"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Which version of `@bitvea/feedback-toolbar` `init` writes into a host app.
|
|
2
|
+
//
|
|
3
|
+
// This used to be one literal in `init.ts`, rewritten by the toolbar's release
|
|
4
|
+
// script at publish time. That coupling could not survive the split - a
|
|
5
|
+
// release script editing another package's source is a boundary in name only,
|
|
6
|
+
// and a CLI published between two toolbar releases would carry a number
|
|
7
|
+
// nothing could tell it was stale. So the number is asked for, in this order,
|
|
8
|
+
// and the order is the whole design:
|
|
9
|
+
//
|
|
10
|
+
// 1. `--toolbar-version <range>`, for pinning deliberately, and for tests.
|
|
11
|
+
// 2. FEEDBACK_TOOLBAR_VERSION in the environment, for a CI that pins.
|
|
12
|
+
// 3. What the launcher passed, which it read out of the installed
|
|
13
|
+
// `@bitvea/feedback-toolbar/package.json` sitting next to it. This is the
|
|
14
|
+
// normal path - `npx @bitvea/feedback-toolbar init` - and it is correct
|
|
15
|
+
// by construction: whatever version npx just resolved is the version
|
|
16
|
+
// written into the host app, including `@1.1.2`, `@pr-42` and an old pin.
|
|
17
|
+
// The baked literal got exactly those cases wrong and could not express
|
|
18
|
+
// them at all.
|
|
19
|
+
// 4. The registry, once, with a short timeout. This is the
|
|
20
|
+
// `npx @bitvea/feedback-cli init` path, where there is no toolbar next to
|
|
21
|
+
// us to ask. `init` on that path is interactive and has network by
|
|
22
|
+
// definition, since npx just used it.
|
|
23
|
+
//
|
|
24
|
+
// And then it REFUSES. There is deliberately no fallback constant: a fallback
|
|
25
|
+
// is the stale literal wearing a different name, and it would be stale exactly
|
|
26
|
+
// on the path that has no launcher to correct it. `init` already refuses
|
|
27
|
+
// before touching the tree for everything else that can fail, and a range is
|
|
28
|
+
// the most consequential thing it writes into a stranger's committed
|
|
29
|
+
// package.json - so a refusal that names `--toolbar-version` beats a wrong
|
|
30
|
+
// number committed under their name.
|
|
31
|
+
/** The package whose version is being resolved. Never imported, only named. */
|
|
32
|
+
const TOOLBAR_PACKAGE = "@bitvea/feedback-toolbar";
|
|
33
|
+
const REGISTRY_URL = `https://registry.npmjs.org/${TOOLBAR_PACKAGE}/latest`;
|
|
34
|
+
/**
|
|
35
|
+
* Long enough for a warm registry, short enough that a machine with no route
|
|
36
|
+
* to npm reaches the refusal while somebody is still watching the terminal.
|
|
37
|
+
*/
|
|
38
|
+
const REGISTRY_TIMEOUT_MS = 3_000;
|
|
39
|
+
export const TOOLBAR_VERSION_ENV_VAR = "FEEDBACK_TOOLBAR_VERSION";
|
|
40
|
+
/**
|
|
41
|
+
* A refusal the person running the command can act on, as opposed to a bug.
|
|
42
|
+
* Carries the remedy, because the remedy is a flag they can type immediately.
|
|
43
|
+
*/
|
|
44
|
+
export class ToolbarVersionError extends Error {
|
|
45
|
+
constructor(message) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = "ToolbarVersionError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export async function resolveToolbarRange(input) {
|
|
51
|
+
if (input.flag)
|
|
52
|
+
return { range: asRange(input.flag), source: "flag" };
|
|
53
|
+
const fromEnv = input.env[TOOLBAR_VERSION_ENV_VAR];
|
|
54
|
+
if (fromEnv)
|
|
55
|
+
return { range: asRange(fromEnv), source: "env" };
|
|
56
|
+
if (input.launcherVersion) {
|
|
57
|
+
return { range: asRange(input.launcherVersion), source: "launcher" };
|
|
58
|
+
}
|
|
59
|
+
const latest = await (input.fetchLatestVersion ?? fetchLatestVersion)();
|
|
60
|
+
if (latest)
|
|
61
|
+
return { range: asRange(latest), source: "registry" };
|
|
62
|
+
throw new ToolbarVersionError(`could not tell which version of ${TOOLBAR_PACKAGE} to install, and this ` +
|
|
63
|
+
"command will not guess one: the range goes into your committed " +
|
|
64
|
+
"package.json.\n" +
|
|
65
|
+
"Pass it: --toolbar-version ^1.2.0 (or set " +
|
|
66
|
+
`${TOOLBAR_VERSION_ENV_VAR}).`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A bare `x.y.z` becomes `^x.y.z`; anything that already reads as a range is
|
|
70
|
+
* written through untouched.
|
|
71
|
+
*
|
|
72
|
+
* Untouched rather than normalised, because the shapes a person reaches for
|
|
73
|
+
* here - an exact pin, a `~`, a dist-tag like `next` - are all things they
|
|
74
|
+
* mean, and quietly turning a pin into a caret would be this command
|
|
75
|
+
* overruling the one decision it was explicitly told.
|
|
76
|
+
*/
|
|
77
|
+
export function asRange(value) {
|
|
78
|
+
const trimmed = value.trim();
|
|
79
|
+
return /^\d+\.\d+\.\d+(?:[-+].+)?$/.test(trimmed) ? `^${trimmed}` : trimmed;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* One GET of the registry's `latest` document. Every failure - no network, a
|
|
83
|
+
* proxy, a 404, a body that is not what we expect - is the same answer here:
|
|
84
|
+
* nothing, so the caller refuses. Nothing is inferred from a failed lookup.
|
|
85
|
+
*/
|
|
86
|
+
async function fetchLatestVersion() {
|
|
87
|
+
try {
|
|
88
|
+
const res = await fetch(REGISTRY_URL, {
|
|
89
|
+
headers: { accept: "application/json" },
|
|
90
|
+
signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS),
|
|
91
|
+
});
|
|
92
|
+
if (!res.ok)
|
|
93
|
+
return undefined;
|
|
94
|
+
const body = await res.json();
|
|
95
|
+
if (body !== null && typeof body === "object" && "version" in body) {
|
|
96
|
+
const version = body.version;
|
|
97
|
+
if (typeof version === "string" && version.length > 0)
|
|
98
|
+
return version;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=toolbarVersion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbarVersion.js","sourceRoot":"","sources":["../src/toolbarVersion.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bitvea/feedback-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "BitVea Feedback installer: install the toolbar into an app, and register a deployment as a project",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"feedback-cli": "./dist/bin.js"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=20"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^24.0.0",
|
|
24
|
+
"eslint": "^10.9.1",
|
|
25
|
+
"typescript": "^6.0.0",
|
|
26
|
+
"vitest": "^2.1.9",
|
|
27
|
+
"@bitvea/release-tools": "0.0.0",
|
|
28
|
+
"@bitvea/config": "0.0.0"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -p tsconfig.build.json && node scripts/add-emitted-import-extensions.mjs",
|
|
39
|
+
"check-publishable": "node scripts/check-publishable.mjs",
|
|
40
|
+
"release": "node scripts/release.mjs",
|
|
41
|
+
"type-check": "tsc -p tsconfig.json --noEmit",
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"test": "vitest run --passWithNoTests",
|
|
44
|
+
"clean": "rm -rf dist .turbo *.tsbuildinfo"
|
|
45
|
+
}
|
|
46
|
+
}
|