@nulab/bee 0.0.0 → 1.0.0-rc.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 +36 -0
- package/bin/cli.mjs +2 -0
- package/dist/chunks/activities.mjs +90 -0
- package/dist/chunks/activities2.mjs +91 -0
- package/dist/chunks/activities3.mjs +91 -0
- package/dist/chunks/add-user.mjs +42 -0
- package/dist/chunks/add.mjs +62 -0
- package/dist/chunks/add2.mjs +40 -0
- package/dist/chunks/api.mjs +138 -0
- package/dist/chunks/attachments.mjs +48 -0
- package/dist/chunks/attachments2.mjs +48 -0
- package/dist/chunks/attachments3.mjs +49 -0
- package/dist/chunks/browse.mjs +270 -0
- package/dist/chunks/clone.mjs +61 -0
- package/dist/chunks/close.mjs +51 -0
- package/dist/chunks/comment.mjs +124 -0
- package/dist/chunks/comment2.mjs +112 -0
- package/dist/chunks/comments.mjs +58 -0
- package/dist/chunks/completion.mjs +118 -0
- package/dist/chunks/count.mjs +74 -0
- package/dist/chunks/count2.mjs +70 -0
- package/dist/chunks/count3.mjs +60 -0
- package/dist/chunks/count4.mjs +35 -0
- package/dist/chunks/count5.mjs +53 -0
- package/dist/chunks/create.mjs +61 -0
- package/dist/chunks/create2.mjs +83 -0
- package/dist/chunks/create3.mjs +65 -0
- package/dist/chunks/create4.mjs +74 -0
- package/dist/chunks/create5.mjs +57 -0
- package/dist/chunks/create6.mjs +38 -0
- package/dist/chunks/create7.mjs +46 -0
- package/dist/chunks/create8.mjs +50 -0
- package/dist/chunks/create9.mjs +50 -0
- package/dist/chunks/dashboard.mjs +85 -0
- package/dist/chunks/delete.mjs +48 -0
- package/dist/chunks/delete2.mjs +47 -0
- package/dist/chunks/delete3.mjs +47 -0
- package/dist/chunks/delete4.mjs +47 -0
- package/dist/chunks/delete5.mjs +48 -0
- package/dist/chunks/delete6.mjs +48 -0
- package/dist/chunks/delete7.mjs +55 -0
- package/dist/chunks/delete8.mjs +54 -0
- package/dist/chunks/delete9.mjs +49 -0
- package/dist/chunks/edit.mjs +56 -0
- package/dist/chunks/edit2.mjs +73 -0
- package/dist/chunks/edit3.mjs +65 -0
- package/dist/chunks/edit4.mjs +52 -0
- package/dist/chunks/edit5.mjs +38 -0
- package/dist/chunks/edit6.mjs +51 -0
- package/dist/chunks/edit7.mjs +47 -0
- package/dist/chunks/edit8.mjs +47 -0
- package/dist/chunks/history.mjs +57 -0
- package/dist/chunks/index.mjs +15 -0
- package/dist/chunks/index10.mjs +18 -0
- package/dist/chunks/index11.mjs +13 -0
- package/dist/chunks/index12.mjs +13 -0
- package/dist/chunks/index13.mjs +13 -0
- package/dist/chunks/index14.mjs +8 -0
- package/dist/chunks/index15.mjs +13 -0
- package/dist/chunks/index16.mjs +13 -0
- package/dist/chunks/index17.mjs +14 -0
- package/dist/chunks/index2.mjs +18 -0
- package/dist/chunks/index3.mjs +20 -0
- package/dist/chunks/index4.mjs +15 -0
- package/dist/chunks/index5.mjs +13 -0
- package/dist/chunks/index6.mjs +17 -0
- package/dist/chunks/index7.mjs +8 -0
- package/dist/chunks/index8.mjs +8 -0
- package/dist/chunks/index9.mjs +13 -0
- package/dist/chunks/list.mjs +53 -0
- package/dist/chunks/list10.mjs +44 -0
- package/dist/chunks/list11.mjs +49 -0
- package/dist/chunks/list12.mjs +45 -0
- package/dist/chunks/list13.mjs +45 -0
- package/dist/chunks/list14.mjs +55 -0
- package/dist/chunks/list15.mjs +47 -0
- package/dist/chunks/list2.mjs +95 -0
- package/dist/chunks/list3.mjs +86 -0
- package/dist/chunks/list4.mjs +80 -0
- package/dist/chunks/list5.mjs +78 -0
- package/dist/chunks/list6.mjs +45 -0
- package/dist/chunks/list7.mjs +49 -0
- package/dist/chunks/list8.mjs +47 -0
- package/dist/chunks/list9.mjs +53 -0
- package/dist/chunks/login.mjs +206 -0
- package/dist/chunks/logout.mjs +57 -0
- package/dist/chunks/me.mjs +49 -0
- package/dist/chunks/read-all.mjs +30 -0
- package/dist/chunks/read.mjs +30 -0
- package/dist/chunks/read2.mjs +28 -0
- package/dist/chunks/refresh.mjs +77 -0
- package/dist/chunks/remove-user.mjs +42 -0
- package/dist/chunks/remove.mjs +28 -0
- package/dist/chunks/reopen.mjs +43 -0
- package/dist/chunks/status.mjs +62 -0
- package/dist/chunks/status2.mjs +62 -0
- package/dist/chunks/status3.mjs +67 -0
- package/dist/chunks/switch.mjs +52 -0
- package/dist/chunks/tags.mjs +41 -0
- package/dist/chunks/token.mjs +39 -0
- package/dist/chunks/tree.mjs +61 -0
- package/dist/chunks/users.mjs +48 -0
- package/dist/chunks/view.mjs +58 -0
- package/dist/chunks/view2.mjs +95 -0
- package/dist/chunks/view3.mjs +69 -0
- package/dist/chunks/view4.mjs +71 -0
- package/dist/chunks/view5.mjs +62 -0
- package/dist/chunks/view6.mjs +53 -0
- package/dist/chunks/view7.mjs +49 -0
- package/dist/chunks/view8.mjs +62 -0
- package/dist/chunks/view9.mjs +49 -0
- package/dist/index.mjs +152 -0
- package/dist/shared/bee.-8tk76YJ.mjs +42 -0
- package/dist/shared/bee.BTBGpv4K.mjs +34 -0
- package/dist/shared/bee.BXiuAfjJ.mjs +10 -0
- package/dist/shared/bee.BeQSH2t0.mjs +25 -0
- package/dist/shared/bee.Btmq3TXs.mjs +19 -0
- package/dist/shared/bee.C--ZWPxf.mjs +14 -0
- package/dist/shared/bee.CCNmDHVv.mjs +33 -0
- package/dist/shared/bee.CQ3kBgas.mjs +58 -0
- package/dist/shared/bee.CThuQMit.mjs +124 -0
- package/dist/shared/bee.ChBdZ0cH.mjs +9 -0
- package/dist/shared/bee.CktwmH8R.mjs +25 -0
- package/dist/shared/bee.CzKcWSES.mjs +42 -0
- package/dist/shared/bee.D6yQ6Pqf.mjs +18 -0
- package/dist/shared/bee.DHTPkjMh.mjs +48 -0
- package/dist/shared/bee.DVTuFf-T.mjs +276 -0
- package/dist/shared/bee.DoTvz3uW.mjs +13 -0
- package/dist/shared/bee.Ds2l-nTJ.mjs +308 -0
- package/dist/shared/bee.IM3QELRf.mjs +32 -0
- package/dist/shared/bee.XxOB1Her.mjs +8 -0
- package/dist/shared/bee.n5MYN4a6.mjs +12 -0
- package/dist/shared/bee.sHWg0IFT.mjs +22 -0
- package/package.json +50 -2
- package/.github/workflows/initial-release.yml +0 -23
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const ACTIVITY_LABELS = {
|
|
2
|
+
1: "Issue Created",
|
|
3
|
+
2: "Issue Updated",
|
|
4
|
+
3: "Issue Commented",
|
|
5
|
+
4: "Issue Deleted",
|
|
6
|
+
5: "Wiki Created",
|
|
7
|
+
6: "Wiki Updated",
|
|
8
|
+
7: "Wiki Deleted",
|
|
9
|
+
8: "File Added",
|
|
10
|
+
9: "File Updated",
|
|
11
|
+
10: "File Deleted",
|
|
12
|
+
11: "SVN Committed",
|
|
13
|
+
12: "Git Pushed",
|
|
14
|
+
13: "Git Repo Created",
|
|
15
|
+
14: "Issue Multi-Updated",
|
|
16
|
+
15: "Project User Added",
|
|
17
|
+
16: "Project User Removed",
|
|
18
|
+
17: "Notification Added",
|
|
19
|
+
18: "Pull Request Added",
|
|
20
|
+
19: "Pull Request Updated",
|
|
21
|
+
20: "Pull Request Commented",
|
|
22
|
+
21: "Pull Request Deleted",
|
|
23
|
+
22: "Milestone Added",
|
|
24
|
+
23: "Milestone Updated",
|
|
25
|
+
24: "Milestone Deleted",
|
|
26
|
+
25: "Project Team Added",
|
|
27
|
+
26: "Project Team Deleted",
|
|
28
|
+
34: "Status Deleted",
|
|
29
|
+
35: "Issues Dates Updated",
|
|
30
|
+
47: "Issue Multi-Created"
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { ACTIVITY_LABELS as A };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { colorize } from 'consola/utils';
|
|
3
|
+
|
|
4
|
+
class BeeCommand extends Command {
|
|
5
|
+
beeExamples = [];
|
|
6
|
+
beeEnvVars = [];
|
|
7
|
+
helpInformation() {
|
|
8
|
+
return super.helpInformation() + this._renderExamples() + this._renderEnvVars();
|
|
9
|
+
}
|
|
10
|
+
createCommand(name) {
|
|
11
|
+
return new BeeCommand(name);
|
|
12
|
+
}
|
|
13
|
+
examples(examples) {
|
|
14
|
+
this.beeExamples.push(...examples);
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
envVars(vars) {
|
|
18
|
+
this.beeEnvVars.push(...vars);
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
async addCommands(mods) {
|
|
22
|
+
const resolved = await Promise.all(mods);
|
|
23
|
+
for (const mod of resolved) {
|
|
24
|
+
this.addCommand(mod.default);
|
|
25
|
+
}
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
_renderExamples() {
|
|
29
|
+
if (this.beeExamples.length === 0) {
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
const lines = this.beeExamples.flatMap((ex) => [
|
|
33
|
+
` # ${ex.description}`,
|
|
34
|
+
` $ ${ex.command}`,
|
|
35
|
+
""
|
|
36
|
+
]);
|
|
37
|
+
return `
|
|
38
|
+
${colorize("bold", "EXAMPLES")}
|
|
39
|
+
${lines.join("\n")}`;
|
|
40
|
+
}
|
|
41
|
+
_renderEnvVars() {
|
|
42
|
+
const fromOptions = this.options.filter((opt) => opt.envVar).map((opt) => [opt.envVar, opt.description ?? ""]);
|
|
43
|
+
const vars = [...fromOptions, ...this.beeEnvVars];
|
|
44
|
+
if (vars.length === 0) {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
const maxLen = Math.max(...vars.map(([k]) => k.length));
|
|
48
|
+
const lines = vars.map(([k, d]) => ` ${k.padEnd(maxLen + 3)}${d}`);
|
|
49
|
+
return `
|
|
50
|
+
${colorize("bold", "ENVIRONMENT VARIABLES")}
|
|
51
|
+
${lines.join("\n")}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const ENV_AUTH = [["BACKLOG_API_KEY", "Authenticate with an API key"]];
|
|
55
|
+
const ENV_PROJECT = ["BACKLOG_PROJECT", "Default project ID or project key"];
|
|
56
|
+
const ENV_REPO = ["BACKLOG_REPO", "Default repository name"];
|
|
57
|
+
|
|
58
|
+
export { BeeCommand as B, ENV_AUTH as E, ENV_PROJECT as a, ENV_REPO as b };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import consola from 'consola';
|
|
2
|
+
import { U as UserError } from './bee.XxOB1Her.mjs';
|
|
3
|
+
import 'node:stream/consumers';
|
|
4
|
+
import 'valibot';
|
|
5
|
+
import { Backlog } from 'backlog-js';
|
|
6
|
+
import { r as refreshAccessToken } from './bee.CktwmH8R.mjs';
|
|
7
|
+
import { l as loadConfig } from './bee.DVTuFf-T.mjs';
|
|
8
|
+
import { f as findSpace, u as updateSpaceAuth } from './bee.-8tk76YJ.mjs';
|
|
9
|
+
|
|
10
|
+
const formatResetTime = (epochSeconds) => {
|
|
11
|
+
const date = new Date(epochSeconds * 1e3);
|
|
12
|
+
return date.toString();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const getClient = async (host) => {
|
|
16
|
+
const config = loadConfig();
|
|
17
|
+
const resolvedHost = host ?? config.defaultSpace;
|
|
18
|
+
if (!resolvedHost) {
|
|
19
|
+
throw new UserError("No space configured. Run `bee auth login` to authenticate.");
|
|
20
|
+
}
|
|
21
|
+
const resolved = findSpace(config.spaces, resolvedHost);
|
|
22
|
+
if (!resolved) {
|
|
23
|
+
throw new UserError(
|
|
24
|
+
`Space "${resolvedHost}" not found. Run \`bee auth login\` to authenticate.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
if (resolved.auth.method === "api-key") {
|
|
28
|
+
const client = new Backlog({
|
|
29
|
+
host: resolved.host,
|
|
30
|
+
apiKey: resolved.auth.apiKey
|
|
31
|
+
});
|
|
32
|
+
return { client, host: resolved.host };
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
client: createOAuthClient(resolved.host, resolved.auth),
|
|
36
|
+
host: resolved.host
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const createOAuthClient = (host, oauthAuth) => {
|
|
40
|
+
let currentClient = new Backlog({ host, accessToken: oauthAuth.accessToken });
|
|
41
|
+
let refreshPromise = null;
|
|
42
|
+
const refreshTokenIfNeeded = async () => {
|
|
43
|
+
if (refreshPromise) {
|
|
44
|
+
const result2 = await refreshPromise;
|
|
45
|
+
if (!result2) {
|
|
46
|
+
throw new UserError(
|
|
47
|
+
"OAuth session has expired. Run `bee auth login -m oauth` to re-authenticate."
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const { clientId, clientSecret, refreshToken } = oauthAuth;
|
|
53
|
+
if (!clientId || !clientSecret || !refreshToken) {
|
|
54
|
+
throw new UserError(
|
|
55
|
+
"OAuth credentials are incomplete. Run `bee auth login -m oauth` to re-authenticate."
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
refreshPromise = (async () => {
|
|
59
|
+
try {
|
|
60
|
+
consola.start("Access token expired. Refreshing...");
|
|
61
|
+
const tokenResponse = await refreshAccessToken(host, {
|
|
62
|
+
clientId,
|
|
63
|
+
clientSecret,
|
|
64
|
+
refreshToken
|
|
65
|
+
});
|
|
66
|
+
currentClient = new Backlog({
|
|
67
|
+
host,
|
|
68
|
+
accessToken: tokenResponse.access_token
|
|
69
|
+
});
|
|
70
|
+
updateSpaceAuth(host, {
|
|
71
|
+
method: "oauth",
|
|
72
|
+
accessToken: tokenResponse.access_token,
|
|
73
|
+
refreshToken: tokenResponse.refresh_token,
|
|
74
|
+
clientId,
|
|
75
|
+
clientSecret
|
|
76
|
+
});
|
|
77
|
+
consola.success("Token refreshed successfully.");
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
const result = await refreshPromise;
|
|
84
|
+
refreshPromise = null;
|
|
85
|
+
if (!result) {
|
|
86
|
+
throw new UserError(
|
|
87
|
+
"OAuth session has expired. Run `bee auth login -m oauth` to re-authenticate."
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const proxy = new Proxy(currentClient, {
|
|
92
|
+
get(_target, prop, receiver) {
|
|
93
|
+
const value = Reflect.get(currentClient, prop, receiver);
|
|
94
|
+
if (typeof value !== "function") {
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
return async (...args) => {
|
|
98
|
+
try {
|
|
99
|
+
return await value.apply(currentClient, args);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
handleRateLimitError(error);
|
|
102
|
+
if (isBacklogAuthError(error)) {
|
|
103
|
+
await refreshTokenIfNeeded();
|
|
104
|
+
const retryValue = Reflect.get(currentClient, prop, receiver);
|
|
105
|
+
return await retryValue.apply(currentClient, args);
|
|
106
|
+
}
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
return proxy;
|
|
113
|
+
};
|
|
114
|
+
const isBacklogAuthError = (error) => error instanceof Error && error._name === "BacklogAuthError" && error._status === 401;
|
|
115
|
+
const isBacklogRateLimitError = (error) => error instanceof Error && error._name === "BacklogApiError" && error._status === 429;
|
|
116
|
+
const handleRateLimitError = (error) => {
|
|
117
|
+
if (isBacklogRateLimitError(error)) {
|
|
118
|
+
const resetEpoch = error._response.headers.get("X-RateLimit-Reset");
|
|
119
|
+
const resetMessage = resetEpoch ? `Rate limit resets at ${formatResetTime(Number(resetEpoch))}.` : "Please wait and try again later.";
|
|
120
|
+
throw new UserError(`API rate limit exceeded. ${resetMessage}`);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
export { getClient as g };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { OAuth2 } from 'backlog-js';
|
|
2
|
+
|
|
3
|
+
const exchangeAuthorizationCode = async (host, params) => {
|
|
4
|
+
const oauth2 = new OAuth2({
|
|
5
|
+
clientId: params.clientId,
|
|
6
|
+
clientSecret: params.clientSecret
|
|
7
|
+
});
|
|
8
|
+
return oauth2.getAccessToken({
|
|
9
|
+
host,
|
|
10
|
+
code: params.code,
|
|
11
|
+
redirectUri: params.redirectUri
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
const refreshAccessToken = async (host, params) => {
|
|
15
|
+
const oauth2 = new OAuth2({
|
|
16
|
+
clientId: params.clientId,
|
|
17
|
+
clientSecret: params.clientSecret
|
|
18
|
+
});
|
|
19
|
+
return oauth2.refreshAccessToken({
|
|
20
|
+
host,
|
|
21
|
+
refreshToken: params.refreshToken
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export { exchangeAuthorizationCode as e, refreshAccessToken as r };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import open from 'open';
|
|
2
|
+
|
|
3
|
+
const openUrl = async (url) => {
|
|
4
|
+
await open(url);
|
|
5
|
+
};
|
|
6
|
+
const buildBacklogUrl = (host, path) => `https://${host}${path}`;
|
|
7
|
+
const issueUrl = (host, issueKey) => buildBacklogUrl(host, `/view/${issueKey}`);
|
|
8
|
+
const projectUrl = (host, projectKey) => buildBacklogUrl(host, `/projects/${projectKey}`);
|
|
9
|
+
const pullRequestUrl = (host, projectKey, repoName, prNumber) => buildBacklogUrl(host, `/git/${projectKey}/${repoName}/pullRequests/${prNumber}`);
|
|
10
|
+
const repositoryUrl = (host, projectKey, repoName) => buildBacklogUrl(host, `/git/${projectKey}/${repoName}`);
|
|
11
|
+
const wikiUrl = (host, wikiId) => buildBacklogUrl(host, `/alias/wiki/${wikiId}`);
|
|
12
|
+
const documentUrl = (host, projectKey, documentId) => buildBacklogUrl(host, `/document/${projectKey}/${documentId}`);
|
|
13
|
+
const categoryUrl = (host, categoryId, projectId) => buildBacklogUrl(
|
|
14
|
+
host,
|
|
15
|
+
`/EditComponent.action?component.id=${categoryId}&component.projectId=${projectId}`
|
|
16
|
+
);
|
|
17
|
+
const milestoneUrl = (host, milestoneId) => buildBacklogUrl(host, `/EditVersion.action?version.id=${milestoneId}`);
|
|
18
|
+
const issueTypeUrl = (host, issueTypeId, projectId) => buildBacklogUrl(
|
|
19
|
+
host,
|
|
20
|
+
`/EditIssueType.action?issueType.id=${issueTypeId}&issueType.projectId=${projectId}`
|
|
21
|
+
);
|
|
22
|
+
const statusUrl = (host, projectKey, statusId) => buildBacklogUrl(host, `/projects/${projectKey}/statuses/${statusId}`);
|
|
23
|
+
const dashboardUrl = (host) => buildBacklogUrl(host, "/dashboard");
|
|
24
|
+
const gitBlobUrl = (host, projectKey, repoName, branch, filePath, line) => {
|
|
25
|
+
const url = buildBacklogUrl(host, `/git/${projectKey}/${repoName}/blob/${branch}/${filePath}`);
|
|
26
|
+
return line ? `${url}#${line}` : url;
|
|
27
|
+
};
|
|
28
|
+
const gitTreeUrl = (host, projectKey, repoName, branch, dirPath) => {
|
|
29
|
+
const base = `/git/${projectKey}/${repoName}/tree/${branch}`;
|
|
30
|
+
return buildBacklogUrl(host, dirPath ? `${base}/${dirPath}` : base);
|
|
31
|
+
};
|
|
32
|
+
const gitCommitUrl = (host, projectKey, repoName, commitSha) => buildBacklogUrl(host, `/git/${projectKey}/${repoName}/commit/${commitSha}`);
|
|
33
|
+
const openOrPrintUrl = async (url, noBrowser, log) => {
|
|
34
|
+
if (noBrowser) {
|
|
35
|
+
log.log(url);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
await openUrl(url);
|
|
39
|
+
log.info(`Opening ${url} in your browser.`);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export { gitTreeUrl as a, buildBacklogUrl as b, gitBlobUrl as c, dashboardUrl as d, openUrl as e, documentUrl as f, gitCommitUrl as g, pullRequestUrl as h, issueUrl as i, categoryUrl as j, issueTypeUrl as k, milestoneUrl as m, openOrPrintUrl as o, projectUrl as p, repositoryUrl as r, statusUrl as s, wikiUrl as w };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import consola from 'consola';
|
|
2
|
+
import { s as stringWidth, w as widthPadEnd } from './bee.Ds2l-nTJ.mjs';
|
|
3
|
+
|
|
4
|
+
const printDefinitionList = (items, indent = 4) => {
|
|
5
|
+
const filtered = items.filter(
|
|
6
|
+
(item) => item[1] !== null && item[1] !== void 0 && item[1] !== ""
|
|
7
|
+
);
|
|
8
|
+
if (filtered.length === 0) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const maxLabel = Math.max(...filtered.map(([label]) => stringWidth(label)));
|
|
12
|
+
const pad = " ".repeat(indent);
|
|
13
|
+
for (const [label, value] of filtered) {
|
|
14
|
+
consola.log(`${pad}${widthPadEnd(label, maxLabel)} ${value}`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { printDefinitionList as p };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Option } from 'commander';
|
|
2
|
+
import { p as promptRequired } from './bee.DVTuFf-T.mjs';
|
|
3
|
+
import 'node:stream/consumers';
|
|
4
|
+
import 'valibot';
|
|
5
|
+
import 'consola';
|
|
6
|
+
|
|
7
|
+
class RequiredOption extends Option {
|
|
8
|
+
promptLabel;
|
|
9
|
+
constructor(flags, description, promptLabel) {
|
|
10
|
+
super(flags, `${description} (required)`);
|
|
11
|
+
this.promptLabel = promptLabel ?? description;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const resolveOptions = async (cmd) => {
|
|
15
|
+
const opts = cmd.opts();
|
|
16
|
+
for (const opt of cmd.options) {
|
|
17
|
+
if (opt instanceof RequiredOption) {
|
|
18
|
+
const key = opt.attributeName();
|
|
19
|
+
opts[key] = await promptRequired(`${opt.promptLabel}:`, opts[key]);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const collect = (val, prev) => [...prev, val];
|
|
25
|
+
const collectNum = (val, prev) => [...prev, Number(val)];
|
|
26
|
+
const project = () => new RequiredOption("-p, --project <id>", "Project ID or project key").env("BACKLOG_PROJECT");
|
|
27
|
+
const repo = () => new RequiredOption("-R, --repo <name>", "Repository name or ID").env("BACKLOG_REPO");
|
|
28
|
+
const count = () => new Option("-L, --count <n>", "Number of results (default: 20)");
|
|
29
|
+
const offset = () => new Option("--offset <n>", "Offset for pagination");
|
|
30
|
+
const order = () => new Option("--order <dir>", "Sort order").choices(["asc", "desc"]);
|
|
31
|
+
const minId = () => new Option("--min-id <n>", "Minimum ID for cursor-based pagination");
|
|
32
|
+
const maxId = () => new Option("--max-id <n>", "Maximum ID for cursor-based pagination");
|
|
33
|
+
const keyword = () => new Option("-k, --keyword <text>", "Keyword search");
|
|
34
|
+
const assignee = () => new Option("-a, --assignee <id>", "Assignee user ID. Use @me for yourself.");
|
|
35
|
+
const assigneeList = () => new Option("-a, --assignee <id>", "Assignee user ID (repeatable). Use @me for yourself.").argParser(collect).default([]);
|
|
36
|
+
const issue = () => new Option("--issue <key>", "Issue ID or issue key");
|
|
37
|
+
const notify = () => new Option("--notify <id>", "User IDs to notify (repeatable)").argParser(collectNum).default([]);
|
|
38
|
+
const attachment = () => new Option("--attachment <id>", "Attachment IDs (repeatable)").argParser(collectNum).default([]);
|
|
39
|
+
const comment = () => new Option("-c, --comment <text>", "Comment to add with the update");
|
|
40
|
+
const web = (resource) => new Option("-w, --web", `Open the ${resource} in the browser`);
|
|
41
|
+
const noBrowser = () => new Option("-n, --no-browser", "Print the URL instead of opening the browser");
|
|
42
|
+
const space = () => new Option("-s, --space <hostname>", "Space hostname").env("BACKLOG_SPACE");
|
|
43
|
+
const json = () => new Option(
|
|
44
|
+
"--json [fields]",
|
|
45
|
+
"Output as JSON (optionally filter by field names, comma-separated)"
|
|
46
|
+
).preset("");
|
|
47
|
+
|
|
48
|
+
export { RequiredOption as R, collectNum as a, count as b, collect as c, assigneeList as d, offset as e, assignee as f, notify as g, attachment as h, comment as i, json as j, keyword as k, maxId as l, minId as m, noBrowser as n, order as o, project as p, repo as q, resolveOptions as r, space as s, issue as t, web as w };
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import consola from 'consola';
|
|
2
|
+
import { U as UserError } from './bee.XxOB1Her.mjs';
|
|
3
|
+
import 'node:stream/consumers';
|
|
4
|
+
import * as v from 'valibot';
|
|
5
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { homedir } from 'node:os';
|
|
8
|
+
import { R as RcSchema } from './bee.BeQSH2t0.mjs';
|
|
9
|
+
|
|
10
|
+
const promptRequired = async (label, existing, options) => {
|
|
11
|
+
if (existing !== void 0) {
|
|
12
|
+
if (existing) {
|
|
13
|
+
return existing;
|
|
14
|
+
}
|
|
15
|
+
throw new UserError(`${label.replace(/:$/, "")} is required.`);
|
|
16
|
+
}
|
|
17
|
+
if (!process.stdin.isTTY) {
|
|
18
|
+
throw new UserError(
|
|
19
|
+
`${label.replace(/:$/, "")} is required. Use arguments to provide it in non-interactive mode.`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
const displayLabel = options?.valueHint ? label.replace(/:$/, ` ${options.valueHint}:`) : label;
|
|
23
|
+
const { valueHint: _, ...promptOptions } = options ?? {};
|
|
24
|
+
const value = await consola.prompt(displayLabel, { type: "text", ...promptOptions });
|
|
25
|
+
if (typeof value !== "string" || !value) {
|
|
26
|
+
throw new UserError(`${label.replace(/:$/, "")} is required.`);
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
};
|
|
30
|
+
const confirmOrExit = async (message, skipConfirm) => {
|
|
31
|
+
if (skipConfirm) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (!process.stdin.isTTY) {
|
|
35
|
+
throw new UserError("Confirmation required. Use --yes to skip in non-interactive mode.");
|
|
36
|
+
}
|
|
37
|
+
const confirmed = await consola.prompt(message, { type: "confirm" });
|
|
38
|
+
if (!confirmed) {
|
|
39
|
+
consola.info("Cancelled.");
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
//#region node_modules/.pnpm/flat@6.0.1/node_modules/flat/index.js
|
|
46
|
+
function isBuffer(obj) {
|
|
47
|
+
return obj && obj.constructor && typeof obj.constructor.isBuffer === "function" && obj.constructor.isBuffer(obj);
|
|
48
|
+
}
|
|
49
|
+
function keyIdentity(key) {
|
|
50
|
+
return key;
|
|
51
|
+
}
|
|
52
|
+
function flatten(target, opts) {
|
|
53
|
+
opts = opts || {};
|
|
54
|
+
const delimiter = opts.delimiter || ".";
|
|
55
|
+
const maxDepth = opts.maxDepth;
|
|
56
|
+
const transformKey = opts.transformKey || keyIdentity;
|
|
57
|
+
const output = {};
|
|
58
|
+
function step(object, prev, currentDepth) {
|
|
59
|
+
currentDepth = currentDepth || 1;
|
|
60
|
+
Object.keys(object).forEach(function(key) {
|
|
61
|
+
const value = object[key];
|
|
62
|
+
const isarray = opts.safe && Array.isArray(value);
|
|
63
|
+
const type = Object.prototype.toString.call(value);
|
|
64
|
+
const isbuffer = isBuffer(value);
|
|
65
|
+
const isobject = type === "[object Object]" || type === "[object Array]";
|
|
66
|
+
const newKey = prev ? prev + delimiter + transformKey(key) : transformKey(key);
|
|
67
|
+
if (!isarray && !isbuffer && isobject && Object.keys(value).length && (!opts.maxDepth || currentDepth < maxDepth)) return step(value, newKey, currentDepth + 1);
|
|
68
|
+
output[newKey] = value;
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
step(target);
|
|
72
|
+
return output;
|
|
73
|
+
}
|
|
74
|
+
function unflatten(target, opts) {
|
|
75
|
+
opts = opts || {};
|
|
76
|
+
const delimiter = opts.delimiter || ".";
|
|
77
|
+
const overwrite = opts.overwrite || false;
|
|
78
|
+
const transformKey = opts.transformKey || keyIdentity;
|
|
79
|
+
const result = {};
|
|
80
|
+
if (isBuffer(target) || Object.prototype.toString.call(target) !== "[object Object]") return target;
|
|
81
|
+
function getkey(key) {
|
|
82
|
+
const parsedKey = Number(key);
|
|
83
|
+
return isNaN(parsedKey) || key.indexOf(".") !== -1 || opts.object ? key : parsedKey;
|
|
84
|
+
}
|
|
85
|
+
function addKeys(keyPrefix, recipient, target) {
|
|
86
|
+
return Object.keys(target).reduce(function(result, key) {
|
|
87
|
+
result[keyPrefix + delimiter + key] = target[key];
|
|
88
|
+
return result;
|
|
89
|
+
}, recipient);
|
|
90
|
+
}
|
|
91
|
+
function isEmpty(val) {
|
|
92
|
+
const type = Object.prototype.toString.call(val);
|
|
93
|
+
const isArray = type === "[object Array]";
|
|
94
|
+
const isObject = type === "[object Object]";
|
|
95
|
+
if (!val) return true;
|
|
96
|
+
else if (isArray) return !val.length;
|
|
97
|
+
else if (isObject) return !Object.keys(val).length;
|
|
98
|
+
}
|
|
99
|
+
target = Object.keys(target).reduce(function(result, key) {
|
|
100
|
+
const type = Object.prototype.toString.call(target[key]);
|
|
101
|
+
if (!(type === "[object Object]" || type === "[object Array]") || isEmpty(target[key])) {
|
|
102
|
+
result[key] = target[key];
|
|
103
|
+
return result;
|
|
104
|
+
} else return addKeys(key, result, flatten(target[key], opts));
|
|
105
|
+
}, {});
|
|
106
|
+
Object.keys(target).forEach(function(key) {
|
|
107
|
+
const split = key.split(delimiter).map(transformKey);
|
|
108
|
+
let key1 = getkey(split.shift());
|
|
109
|
+
let key2 = getkey(split[0]);
|
|
110
|
+
let recipient = result;
|
|
111
|
+
while (key2 !== void 0) {
|
|
112
|
+
if (key1 === "__proto__") return;
|
|
113
|
+
const type = Object.prototype.toString.call(recipient[key1]);
|
|
114
|
+
const isobject = type === "[object Object]" || type === "[object Array]";
|
|
115
|
+
if (!overwrite && !isobject && typeof recipient[key1] !== "undefined") return;
|
|
116
|
+
if (overwrite && !isobject || !overwrite && recipient[key1] == null) recipient[key1] = typeof key2 === "number" && !opts.object ? [] : {};
|
|
117
|
+
recipient = recipient[key1];
|
|
118
|
+
if (split.length > 0) {
|
|
119
|
+
key1 = getkey(split.shift());
|
|
120
|
+
key2 = getkey(split[0]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
recipient[key1] = unflatten(target[key], opts);
|
|
124
|
+
});
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
|
|
129
|
+
const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
|
|
130
|
+
const JsonSigRx = /^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;
|
|
131
|
+
function jsonParseTransform(key, value) {
|
|
132
|
+
if (key === "__proto__" || key === "constructor" && value && typeof value === "object" && "prototype" in value) {
|
|
133
|
+
warnKeyDropped(key);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
function warnKeyDropped(key) {
|
|
139
|
+
console.warn(`[destr] Dropping "${key}" key to prevent prototype pollution.`);
|
|
140
|
+
}
|
|
141
|
+
function destr(value, options = {}) {
|
|
142
|
+
if (typeof value !== "string") {
|
|
143
|
+
return value;
|
|
144
|
+
}
|
|
145
|
+
if (value[0] === '"' && value[value.length - 1] === '"' && value.indexOf("\\") === -1) {
|
|
146
|
+
return value.slice(1, -1);
|
|
147
|
+
}
|
|
148
|
+
const _value = value.trim();
|
|
149
|
+
if (_value.length <= 9) {
|
|
150
|
+
switch (_value.toLowerCase()) {
|
|
151
|
+
case "true": {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
case "false": {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
case "undefined": {
|
|
158
|
+
return void 0;
|
|
159
|
+
}
|
|
160
|
+
case "null": {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
case "nan": {
|
|
164
|
+
return Number.NaN;
|
|
165
|
+
}
|
|
166
|
+
case "infinity": {
|
|
167
|
+
return Number.POSITIVE_INFINITY;
|
|
168
|
+
}
|
|
169
|
+
case "-infinity": {
|
|
170
|
+
return Number.NEGATIVE_INFINITY;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (!JsonSigRx.test(value)) {
|
|
175
|
+
if (options.strict) {
|
|
176
|
+
throw new SyntaxError("[destr] Invalid JSON");
|
|
177
|
+
}
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) {
|
|
182
|
+
if (options.strict) {
|
|
183
|
+
throw new Error("[destr] Possible prototype pollution");
|
|
184
|
+
}
|
|
185
|
+
return JSON.parse(value, jsonParseTransform);
|
|
186
|
+
}
|
|
187
|
+
return JSON.parse(value);
|
|
188
|
+
} catch (error) {
|
|
189
|
+
if (options.strict) {
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
return value;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//#region src/index.ts
|
|
197
|
+
const RE_KEY_VAL = /^\s*([^\s=]+)\s*=\s*(.*)?\s*$/;
|
|
198
|
+
const RE_LINES = /\n|\r|\r\n/;
|
|
199
|
+
const defaults = {
|
|
200
|
+
name: ".conf",
|
|
201
|
+
dir: process.cwd(),
|
|
202
|
+
flat: false
|
|
203
|
+
};
|
|
204
|
+
function withDefaults(options) {
|
|
205
|
+
if (typeof options === "string") options = { name: options };
|
|
206
|
+
return {
|
|
207
|
+
...defaults,
|
|
208
|
+
...options
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function parse(contents, options = {}) {
|
|
212
|
+
const config = {};
|
|
213
|
+
const lines = contents.split(RE_LINES);
|
|
214
|
+
for (const line of lines) {
|
|
215
|
+
const match = line.match(RE_KEY_VAL);
|
|
216
|
+
if (!match) continue;
|
|
217
|
+
const key = match[1];
|
|
218
|
+
if (!key || key === "__proto__" || key === "constructor") continue;
|
|
219
|
+
const value = destr((match[2] || "").trim());
|
|
220
|
+
if (key.endsWith("[]")) {
|
|
221
|
+
const nkey = key.slice(0, Math.max(0, key.length - 2));
|
|
222
|
+
config[nkey] = (config[nkey] || []).concat(value);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
config[key] = value;
|
|
226
|
+
}
|
|
227
|
+
return options.flat ? config : unflatten(config, { overwrite: true });
|
|
228
|
+
}
|
|
229
|
+
function parseFile(path, options) {
|
|
230
|
+
if (!existsSync(path)) return {};
|
|
231
|
+
return parse(readFileSync(path, "utf8"), options);
|
|
232
|
+
}
|
|
233
|
+
function read(options) {
|
|
234
|
+
options = withDefaults(options);
|
|
235
|
+
return parseFile(resolve(options.dir, options.name), options);
|
|
236
|
+
}
|
|
237
|
+
function readUser(options) {
|
|
238
|
+
options = withDefaults(options);
|
|
239
|
+
options.dir = process.env.XDG_CONFIG_HOME || homedir();
|
|
240
|
+
return read(options);
|
|
241
|
+
}
|
|
242
|
+
function serialize(config) {
|
|
243
|
+
return Object.entries(flatten(config)).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
244
|
+
}
|
|
245
|
+
function write(config, options) {
|
|
246
|
+
options = withDefaults(options);
|
|
247
|
+
writeFileSync(resolve(options.dir, options.name), serialize(config), { encoding: "utf8" });
|
|
248
|
+
}
|
|
249
|
+
function writeUser(config, options) {
|
|
250
|
+
options = withDefaults(options);
|
|
251
|
+
options.dir = process.env.XDG_CONFIG_HOME || homedir();
|
|
252
|
+
write(config, options);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const CONFIG_FILE_NAME = ".beerc";
|
|
256
|
+
const loadConfig = () => {
|
|
257
|
+
const raw = readUser(CONFIG_FILE_NAME);
|
|
258
|
+
const result = v.safeParse(RcSchema, raw);
|
|
259
|
+
if (!result.success) {
|
|
260
|
+
const details = result.issues.map((issue) => issue.message).join("\n");
|
|
261
|
+
throw new UserError(`Configuration Error:
|
|
262
|
+
${details}`);
|
|
263
|
+
}
|
|
264
|
+
return result.output;
|
|
265
|
+
};
|
|
266
|
+
const writeConfig = (config) => {
|
|
267
|
+
writeUser(config, CONFIG_FILE_NAME);
|
|
268
|
+
};
|
|
269
|
+
const updateConfig = (updater) => {
|
|
270
|
+
const config = loadConfig();
|
|
271
|
+
const updated = updater(config);
|
|
272
|
+
writeConfig(updated);
|
|
273
|
+
return updated;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
export { confirmOrExit as c, loadConfig as l, promptRequired as p, updateConfig as u, writeConfig as w };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const PrStatusId = {
|
|
2
|
+
Open: 1,
|
|
3
|
+
Closed: 2,
|
|
4
|
+
Merged: 3
|
|
5
|
+
};
|
|
6
|
+
const PrStatusName = {
|
|
7
|
+
open: PrStatusId.Open,
|
|
8
|
+
closed: PrStatusId.Closed,
|
|
9
|
+
merged: PrStatusId.Merged
|
|
10
|
+
};
|
|
11
|
+
const PR_STATUS_NAMES = Object.keys(PrStatusName);
|
|
12
|
+
|
|
13
|
+
export { PrStatusName as P, PR_STATUS_NAMES as a, PrStatusId as b };
|