azdo-cli 0.24.0-develop.901 → 0.24.0-develop.915
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/dist/index.js +239 -48
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -4296,7 +4296,7 @@ async function getPullRequestThread(context, repo, cred, prId, threadId) {
|
|
|
4296
4296
|
const data = await readJsonResponse(response);
|
|
4297
4297
|
return toActiveCommentThread(data);
|
|
4298
4298
|
}
|
|
4299
|
-
async function createPullRequestThread(context, repo, cred, prId, content, status2) {
|
|
4299
|
+
async function createPullRequestThread(context, repo, cred, prId, content, status2, anchor) {
|
|
4300
4300
|
const url = new URL(
|
|
4301
4301
|
`https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullRequests/${prId}/threads`
|
|
4302
4302
|
);
|
|
@@ -4307,6 +4307,10 @@ async function createPullRequestThread(context, repo, cred, prId, content, statu
|
|
|
4307
4307
|
if (status2 !== void 0) {
|
|
4308
4308
|
payload.status = status2;
|
|
4309
4309
|
}
|
|
4310
|
+
if (anchor !== void 0) {
|
|
4311
|
+
payload.threadContext = anchor.threadContext;
|
|
4312
|
+
payload.pullRequestThreadContext = anchor.pullRequestThreadContext;
|
|
4313
|
+
}
|
|
4310
4314
|
const response = await fetchWithErrors(url.toString(), {
|
|
4311
4315
|
method: "POST",
|
|
4312
4316
|
headers: {
|
|
@@ -4318,6 +4322,42 @@ async function createPullRequestThread(context, repo, cred, prId, content, statu
|
|
|
4318
4322
|
const data = await readJsonResponse(response);
|
|
4319
4323
|
return toActiveCommentThread(data);
|
|
4320
4324
|
}
|
|
4325
|
+
function buildIterationsUrl(context, repo, prId, suffix = "") {
|
|
4326
|
+
const url = new URL(
|
|
4327
|
+
`https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullRequests/${prId}/iterations${suffix}`
|
|
4328
|
+
);
|
|
4329
|
+
url.searchParams.set("api-version", "7.1");
|
|
4330
|
+
return url;
|
|
4331
|
+
}
|
|
4332
|
+
async function getLatestPullRequestIterationId(context, repo, cred, prId) {
|
|
4333
|
+
const url = buildIterationsUrl(context, repo, prId);
|
|
4334
|
+
const response = await fetchWithErrors(url.toString(), { headers: authHeaders(cred) });
|
|
4335
|
+
const data = await readJsonResponse(response);
|
|
4336
|
+
const ids = (data.value ?? []).map((iteration) => iteration.id);
|
|
4337
|
+
if (ids.length === 0) {
|
|
4338
|
+
throw new Error(`Pull request #${prId} has no iterations.`);
|
|
4339
|
+
}
|
|
4340
|
+
return Math.max(...ids);
|
|
4341
|
+
}
|
|
4342
|
+
async function getPullRequestIterationChanges(context, repo, cred, prId, iterationId) {
|
|
4343
|
+
const changes = [];
|
|
4344
|
+
let skip = 0;
|
|
4345
|
+
for (; ; ) {
|
|
4346
|
+
const url = buildIterationsUrl(context, repo, prId, `/${iterationId}/changes`);
|
|
4347
|
+
url.searchParams.set("$top", "2000");
|
|
4348
|
+
if (skip > 0) {
|
|
4349
|
+
url.searchParams.set("$skip", String(skip));
|
|
4350
|
+
}
|
|
4351
|
+
const response = await fetchWithErrors(url.toString(), { headers: authHeaders(cred) });
|
|
4352
|
+
const page = await readJsonResponse(response);
|
|
4353
|
+
changes.push(...page.changeEntries ?? []);
|
|
4354
|
+
const next = page.nextSkip ?? 0;
|
|
4355
|
+
if (next <= skip) {
|
|
4356
|
+
return changes;
|
|
4357
|
+
}
|
|
4358
|
+
skip = next;
|
|
4359
|
+
}
|
|
4360
|
+
}
|
|
4321
4361
|
function buildThreadCommentUrl(context, repo, prId, threadId) {
|
|
4322
4362
|
const url = new URL(
|
|
4323
4363
|
`https://dev.azure.com/${encodeURIComponent(context.org)}/${encodeURIComponent(context.project)}/_apis/git/repositories/${encodeURIComponent(repo)}/pullRequests/${prId}/threads/${threadId}/comments`
|
|
@@ -4641,6 +4681,39 @@ async function resolvePullRequestTemplate(context, repo, cred, defaultBranch, ta
|
|
|
4641
4681
|
return firstMatchingTemplate(context, repo, cred, defaultBranch, defaultTemplateCandidates(), "default");
|
|
4642
4682
|
}
|
|
4643
4683
|
|
|
4684
|
+
// src/services/thread-anchor.ts
|
|
4685
|
+
var LINE_END_OFFSET = 1e3;
|
|
4686
|
+
function normalizeThreadFilePath(raw) {
|
|
4687
|
+
if (raw.trim() === "") {
|
|
4688
|
+
return null;
|
|
4689
|
+
}
|
|
4690
|
+
let path5 = raw.replaceAll("\\", "/");
|
|
4691
|
+
while (path5.startsWith("./")) {
|
|
4692
|
+
path5 = path5.slice(2);
|
|
4693
|
+
}
|
|
4694
|
+
path5 = path5.replace(/^\/+/, "");
|
|
4695
|
+
return path5 === "" ? null : `/${path5}`;
|
|
4696
|
+
}
|
|
4697
|
+
function buildThreadAnchorContext(filePath, line, endLine, side) {
|
|
4698
|
+
const start = { line, offset: 1 };
|
|
4699
|
+
const end = { line: endLine, offset: LINE_END_OFFSET };
|
|
4700
|
+
return side === "right" ? { filePath, rightFileStart: start, rightFileEnd: end } : { filePath, leftFileStart: start, leftFileEnd: end };
|
|
4701
|
+
}
|
|
4702
|
+
function buildPullRequestThreadContext(iterationId, changeTrackingId) {
|
|
4703
|
+
const context = {
|
|
4704
|
+
iterationContext: { firstComparingIteration: iterationId, secondComparingIteration: iterationId }
|
|
4705
|
+
};
|
|
4706
|
+
if (changeTrackingId !== void 0) {
|
|
4707
|
+
context.changeTrackingId = changeTrackingId;
|
|
4708
|
+
}
|
|
4709
|
+
return context;
|
|
4710
|
+
}
|
|
4711
|
+
function findChangeTrackingId(changes, filePath, side) {
|
|
4712
|
+
const byPath = (change) => change.item?.path === filePath;
|
|
4713
|
+
const match = side === "left" ? changes.find((change) => change.originalPath === filePath) ?? changes.find(byPath) : changes.find(byPath);
|
|
4714
|
+
return match?.changeTrackingId;
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4644
4717
|
// src/commands/pr.ts
|
|
4645
4718
|
function parsePositivePrNumber(raw) {
|
|
4646
4719
|
if (!/^\d+$/.test(raw)) {
|
|
@@ -5794,6 +5867,130 @@ var CREATABLE_THREAD_STATUSES = [
|
|
|
5794
5867
|
"byDesign",
|
|
5795
5868
|
"pending"
|
|
5796
5869
|
];
|
|
5870
|
+
var THREAD_ANCHOR_SIDES = ["right", "left"];
|
|
5871
|
+
function parseCommentAddStatus(raw) {
|
|
5872
|
+
if (raw === void 0) {
|
|
5873
|
+
return void 0;
|
|
5874
|
+
}
|
|
5875
|
+
const match = CREATABLE_THREAD_STATUSES.find((candidate) => candidate === raw);
|
|
5876
|
+
if (match === void 0) {
|
|
5877
|
+
writeError(`Invalid --status "${raw}"; expected one of ${CREATABLE_THREAD_STATUSES.join(", ")}.`);
|
|
5878
|
+
return null;
|
|
5879
|
+
}
|
|
5880
|
+
return match;
|
|
5881
|
+
}
|
|
5882
|
+
function parseLineOption(flag, raw) {
|
|
5883
|
+
const value = parsePositivePrNumber(raw);
|
|
5884
|
+
if (value === null) {
|
|
5885
|
+
writeError(`Invalid ${flag} "${raw}"; expected a positive integer (1-based line number).`);
|
|
5886
|
+
}
|
|
5887
|
+
return value;
|
|
5888
|
+
}
|
|
5889
|
+
function parseCommentAnchorOptions(options) {
|
|
5890
|
+
if (options.filePath === void 0) {
|
|
5891
|
+
const stray = [
|
|
5892
|
+
["--line", options.line],
|
|
5893
|
+
["--end-line", options.endLine],
|
|
5894
|
+
["--side", options.side],
|
|
5895
|
+
["--allow-unchanged-file", options.allowUnchangedFile]
|
|
5896
|
+
].find(([, value]) => value !== void 0);
|
|
5897
|
+
if (stray !== void 0) {
|
|
5898
|
+
writeError(`${stray[0]} requires --file-path.`);
|
|
5899
|
+
return null;
|
|
5900
|
+
}
|
|
5901
|
+
return void 0;
|
|
5902
|
+
}
|
|
5903
|
+
if (options.line === void 0) {
|
|
5904
|
+
writeError("--file-path requires --line.");
|
|
5905
|
+
return null;
|
|
5906
|
+
}
|
|
5907
|
+
const filePath = normalizeThreadFilePath(options.filePath);
|
|
5908
|
+
if (filePath === null) {
|
|
5909
|
+
writeError(`Invalid --file-path "${options.filePath}"; expected a repository file path.`);
|
|
5910
|
+
return null;
|
|
5911
|
+
}
|
|
5912
|
+
const line = parseLineOption("--line", options.line);
|
|
5913
|
+
if (line === null) {
|
|
5914
|
+
return null;
|
|
5915
|
+
}
|
|
5916
|
+
const endLine = options.endLine === void 0 ? line : parseLineOption("--end-line", options.endLine);
|
|
5917
|
+
if (endLine === null) {
|
|
5918
|
+
return null;
|
|
5919
|
+
}
|
|
5920
|
+
if (endLine < line) {
|
|
5921
|
+
writeError(`--end-line (${endLine}) must not be less than --line (${line}).`);
|
|
5922
|
+
return null;
|
|
5923
|
+
}
|
|
5924
|
+
const side = THREAD_ANCHOR_SIDES.find((candidate) => candidate === (options.side ?? "right"));
|
|
5925
|
+
if (side === void 0) {
|
|
5926
|
+
writeError(`Invalid --side "${options.side}"; expected ${THREAD_ANCHOR_SIDES.join(" or ")}.`);
|
|
5927
|
+
return null;
|
|
5928
|
+
}
|
|
5929
|
+
return { filePath, line, endLine, side, allowUnchangedFile: options.allowUnchangedFile === true };
|
|
5930
|
+
}
|
|
5931
|
+
async function resolveCommentAnchor(target, request) {
|
|
5932
|
+
const prId = target.pullRequest.id;
|
|
5933
|
+
const iterationId = await getLatestPullRequestIterationId(target.context, target.repo, target.pat, prId);
|
|
5934
|
+
const changes = await getPullRequestIterationChanges(target.context, target.repo, target.pat, prId, iterationId);
|
|
5935
|
+
const changeTrackingId = findChangeTrackingId(changes, request.filePath, request.side);
|
|
5936
|
+
if (changeTrackingId === void 0 && !request.allowUnchangedFile) {
|
|
5937
|
+
writeError(
|
|
5938
|
+
`${request.filePath} is not among the files changed by pull request #${prId} (iteration ${iterationId}). Check the path, or pass --allow-unchanged-file to post the comment anyway.`,
|
|
5939
|
+
3
|
|
5940
|
+
);
|
|
5941
|
+
return null;
|
|
5942
|
+
}
|
|
5943
|
+
return {
|
|
5944
|
+
request,
|
|
5945
|
+
iterationId,
|
|
5946
|
+
changeTrackingId,
|
|
5947
|
+
threadContext: buildThreadAnchorContext(request.filePath, request.line, request.endLine, request.side),
|
|
5948
|
+
pullRequestThreadContext: buildPullRequestThreadContext(iterationId, changeTrackingId)
|
|
5949
|
+
};
|
|
5950
|
+
}
|
|
5951
|
+
function describeCommentAnchor(anchor) {
|
|
5952
|
+
const { filePath, line, endLine, side } = anchor.request;
|
|
5953
|
+
const lines = line === endLine ? `line ${line}` : `lines ${line}-${endLine}`;
|
|
5954
|
+
const tracking = anchor.changeTrackingId === void 0 ? "no changeTrackingId" : `changeTrackingId ${anchor.changeTrackingId}`;
|
|
5955
|
+
return ` at ${filePath} ${lines} (${side} side, iteration ${anchor.iterationId}, ${tracking})`;
|
|
5956
|
+
}
|
|
5957
|
+
function warnIfUntracked(anchor) {
|
|
5958
|
+
if (anchor !== void 0 && anchor.changeTrackingId === void 0) {
|
|
5959
|
+
process.stderr.write(
|
|
5960
|
+
`Note: ${anchor.request.filePath} is not among the pull request's changed files, so the thread is posted without a changeTrackingId and will not follow the line across later pushes.
|
|
5961
|
+
`
|
|
5962
|
+
);
|
|
5963
|
+
}
|
|
5964
|
+
}
|
|
5965
|
+
function withAnchorFields(result, anchor) {
|
|
5966
|
+
if (anchor === void 0) {
|
|
5967
|
+
return result;
|
|
5968
|
+
}
|
|
5969
|
+
return {
|
|
5970
|
+
...result,
|
|
5971
|
+
threadContext: anchor.threadContext,
|
|
5972
|
+
pullRequestThreadContext: anchor.pullRequestThreadContext,
|
|
5973
|
+
changeTracked: anchor.changeTrackingId !== void 0
|
|
5974
|
+
};
|
|
5975
|
+
}
|
|
5976
|
+
function writeCommentAddDryRun(prId, body, status2, anchor, json) {
|
|
5977
|
+
if (json) {
|
|
5978
|
+
const dryResult = withAnchorFields(
|
|
5979
|
+
{ pullRequestId: prId, threadId: null, commentId: null, status: status2 ?? null, content: body, dryRun: true },
|
|
5980
|
+
anchor
|
|
5981
|
+
);
|
|
5982
|
+
process.stdout.write(`${JSON.stringify(dryResult, null, 2)}
|
|
5983
|
+
`);
|
|
5984
|
+
return;
|
|
5985
|
+
}
|
|
5986
|
+
const statusSuffix = status2 === void 0 ? "" : ` with status ${status2}`;
|
|
5987
|
+
const anchorSuffix = anchor === void 0 ? "" : describeCommentAnchor(anchor);
|
|
5988
|
+
process.stdout.write(
|
|
5989
|
+
`Dry run: would post a new comment thread${statusSuffix} on pull request #${prId}${anchorSuffix} (${body.length} chars).
|
|
5990
|
+
${body}
|
|
5991
|
+
`
|
|
5992
|
+
);
|
|
5993
|
+
}
|
|
5797
5994
|
async function runCommentAdd(text, options) {
|
|
5798
5995
|
let context;
|
|
5799
5996
|
try {
|
|
@@ -5801,16 +5998,13 @@ async function runCommentAdd(text, options) {
|
|
|
5801
5998
|
if (body === null) {
|
|
5802
5999
|
return;
|
|
5803
6000
|
}
|
|
5804
|
-
|
|
5805
|
-
if (
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
return;
|
|
5812
|
-
}
|
|
5813
|
-
status2 = match;
|
|
6001
|
+
const status2 = parseCommentAddStatus(options.status);
|
|
6002
|
+
if (status2 === null) {
|
|
6003
|
+
return;
|
|
6004
|
+
}
|
|
6005
|
+
const anchorRequest = parseCommentAnchorOptions(options);
|
|
6006
|
+
if (anchorRequest === null) {
|
|
6007
|
+
return;
|
|
5814
6008
|
}
|
|
5815
6009
|
const target = await resolvePullRequestTarget(options, {
|
|
5816
6010
|
onContextResolved: (resolved) => {
|
|
@@ -5820,26 +6014,13 @@ async function runCommentAdd(text, options) {
|
|
|
5820
6014
|
if (target === null) {
|
|
5821
6015
|
return;
|
|
5822
6016
|
}
|
|
6017
|
+
const anchor = anchorRequest === void 0 ? void 0 : await resolveCommentAnchor(target, anchorRequest);
|
|
6018
|
+
if (anchor === null) {
|
|
6019
|
+
return;
|
|
6020
|
+
}
|
|
6021
|
+
warnIfUntracked(anchor);
|
|
5823
6022
|
if (options.dryRun === true) {
|
|
5824
|
-
|
|
5825
|
-
pullRequestId: target.pullRequest.id,
|
|
5826
|
-
threadId: null,
|
|
5827
|
-
commentId: null,
|
|
5828
|
-
status: status2 ?? null,
|
|
5829
|
-
content: body,
|
|
5830
|
-
dryRun: true
|
|
5831
|
-
};
|
|
5832
|
-
if (options.json) {
|
|
5833
|
-
process.stdout.write(`${JSON.stringify(dryResult, null, 2)}
|
|
5834
|
-
`);
|
|
5835
|
-
return;
|
|
5836
|
-
}
|
|
5837
|
-
const statusSuffix = status2 === void 0 ? "" : ` with status ${status2}`;
|
|
5838
|
-
process.stdout.write(
|
|
5839
|
-
`Dry run: would post a new comment thread${statusSuffix} on pull request #${target.pullRequest.id} (${body.length} chars).
|
|
5840
|
-
${body}
|
|
5841
|
-
`
|
|
5842
|
-
);
|
|
6023
|
+
writeCommentAddDryRun(target.pullRequest.id, body, status2, anchor, options.json);
|
|
5843
6024
|
return;
|
|
5844
6025
|
}
|
|
5845
6026
|
const thread = await createPullRequestThread(
|
|
@@ -5848,24 +6029,29 @@ ${body}
|
|
|
5848
6029
|
target.pat,
|
|
5849
6030
|
target.pullRequest.id,
|
|
5850
6031
|
body,
|
|
5851
|
-
status2
|
|
6032
|
+
status2,
|
|
6033
|
+
...anchor === void 0 ? [] : [{ threadContext: anchor.threadContext, pullRequestThreadContext: anchor.pullRequestThreadContext }]
|
|
5852
6034
|
);
|
|
5853
6035
|
const created = thread.comments[0];
|
|
5854
|
-
const result =
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
6036
|
+
const result = withAnchorFields(
|
|
6037
|
+
{
|
|
6038
|
+
pullRequestId: target.pullRequest.id,
|
|
6039
|
+
threadId: thread.id,
|
|
6040
|
+
commentId: created?.id ?? null,
|
|
6041
|
+
status: thread.status,
|
|
6042
|
+
content: created?.content ?? body,
|
|
6043
|
+
dryRun: false
|
|
6044
|
+
},
|
|
6045
|
+
anchor
|
|
6046
|
+
);
|
|
5862
6047
|
if (options.json) {
|
|
5863
6048
|
process.stdout.write(`${JSON.stringify(result, null, 2)}
|
|
5864
6049
|
`);
|
|
5865
6050
|
return;
|
|
5866
6051
|
}
|
|
6052
|
+
const anchorSuffix = anchor === void 0 ? "" : describeCommentAnchor(anchor);
|
|
5867
6053
|
process.stdout.write(
|
|
5868
|
-
`Comment posted to pull request #${target.pullRequest.id} (thread #${thread.id}).
|
|
6054
|
+
`Comment posted to pull request #${target.pullRequest.id} (thread #${thread.id})${anchorSuffix}.
|
|
5869
6055
|
`
|
|
5870
6056
|
);
|
|
5871
6057
|
} catch (err) {
|
|
@@ -5877,18 +6063,21 @@ function buildCommentAddCommand(name, description) {
|
|
|
5877
6063
|
withCommonPrOptions(configureUnwrappedHelp(command)).description(description).argument("[text]", "body of the new comment; omit when using --file").option("--file <path>", "read the comment body from a UTF-8 file instead of the inline argument").option(
|
|
5878
6064
|
"--status <status>",
|
|
5879
6065
|
`thread status (${CREATABLE_THREAD_STATUSES.join(" | ")}); omit for a plain, non-resolvable comment`
|
|
6066
|
+
).option("--file-path <path>", "anchor the thread to this repository file (Files tab); requires --line").option("--line <n>", "first line of the anchored range (1-based)").option("--end-line <n>", "last line of the anchored range (default: --line)").option("--side <side>", "right (new version, default) or left (old version, for deleted lines)").option(
|
|
6067
|
+
"--allow-unchanged-file",
|
|
6068
|
+
"post even when --file-path is not among the pull request's changed files (the thread will not follow the line across pushes)"
|
|
5880
6069
|
).option("--dry-run", "resolve the target pull request and print what would be posted, without writing anything").option("--pr-number <N>", PR_NUMBER_HELP).option("--json", "output JSON").action(async (text, _options, command2) => {
|
|
5881
6070
|
await runCommentAdd(text, mergedPrOptions(command2));
|
|
5882
6071
|
});
|
|
5883
6072
|
return command;
|
|
5884
6073
|
}
|
|
5885
6074
|
function createPrCommentsAddCommand() {
|
|
5886
|
-
return buildCommentAddCommand("add", "Post a new comment thread on the pull request overview");
|
|
6075
|
+
return buildCommentAddCommand("add", "Post a new comment thread on the pull request overview, or on a file and line");
|
|
5887
6076
|
}
|
|
5888
6077
|
function createPrCommentAddCommand() {
|
|
5889
6078
|
return buildCommentAddCommand(
|
|
5890
6079
|
"comment-add",
|
|
5891
|
-
'Post a new comment thread on the pull request overview (alias of "azdo pr comments add")'
|
|
6080
|
+
'Post a new comment thread on the pull request overview, or on a file and line (alias of "azdo pr comments add")'
|
|
5892
6081
|
);
|
|
5893
6082
|
}
|
|
5894
6083
|
function parseExplicitCommentId(options) {
|
|
@@ -6178,13 +6367,15 @@ async function attachWorkItemIds(context, repo, cred, pullRequests) {
|
|
|
6178
6367
|
const result = new Array(pullRequests.length);
|
|
6179
6368
|
let next = 0;
|
|
6180
6369
|
const worker = async () => {
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
next += 1;
|
|
6184
|
-
const pullRequest = pullRequests[index];
|
|
6185
|
-
const workItemIds = await getPullRequestWorkItemIds(context, repo, cred, pullRequest.id);
|
|
6186
|
-
result[index] = { ...pullRequest, workItemIds };
|
|
6370
|
+
if (next >= pullRequests.length) {
|
|
6371
|
+
return;
|
|
6187
6372
|
}
|
|
6373
|
+
const index = next;
|
|
6374
|
+
next += 1;
|
|
6375
|
+
const pullRequest = pullRequests[index];
|
|
6376
|
+
const workItemIds = await getPullRequestWorkItemIds(context, repo, cred, pullRequest.id);
|
|
6377
|
+
result[index] = { ...pullRequest, workItemIds };
|
|
6378
|
+
await worker();
|
|
6188
6379
|
};
|
|
6189
6380
|
await Promise.all(Array.from({ length: Math.min(WORK_ITEM_LOOKUP_CONCURRENCY, pullRequests.length) }, worker));
|
|
6190
6381
|
return result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azdo-cli",
|
|
3
|
-
"version": "0.24.0-develop.
|
|
3
|
+
"version": "0.24.0-develop.915",
|
|
4
4
|
"description": "Azure DevOps CLI tool",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsup",
|
|
14
14
|
"lint": "eslint src/",
|
|
15
|
+
"sonar:local": "bash scripts/sonar-local.sh",
|
|
16
|
+
"sonar:sync": "node scripts/sonar-profile-sync.mjs",
|
|
15
17
|
"typecheck": "tsc --noEmit",
|
|
16
18
|
"format": "prettier --check src/",
|
|
17
19
|
"test": "npm run typecheck && npm run lint && npm run build && vitest run tests/unit tests/integration",
|
|
@@ -39,6 +41,9 @@
|
|
|
39
41
|
"@eslint/js": "^10.0.1",
|
|
40
42
|
"@types/node": "^25.9.8",
|
|
41
43
|
"eslint": "^10.11.0",
|
|
44
|
+
"eslint-plugin-sonarjs": "^4.2.2",
|
|
45
|
+
"eslint-plugin-unicorn": "^78.0.0",
|
|
46
|
+
"jscpd": "^5.4.1",
|
|
42
47
|
"prettier": "^3.9.9",
|
|
43
48
|
"tsup": "^8.5.1",
|
|
44
49
|
"typescript": "^5.9.3",
|