@smwb/onreel-git-mcp 0.2.0 → 0.3.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 +489 -0
- package/dist/index.d.ts +1 -2
- package/dist/index.js +91 -1
- package/dist/index.js.map +1 -1
- package/dist/tracker/index.js +365 -36
- package/dist/tracker/index.js.map +1 -1
- package/dist/tracker/mapping.const.d.ts +2 -0
- package/dist/tracker/tracker.const.d.ts +12 -0
- package/dist/tracker/tracker.lib.d.ts +26 -1
- package/dist/tracker/tracker.types.d.ts +61 -2
- package/package.json +15 -3
package/dist/tracker/index.js
CHANGED
|
@@ -9,6 +9,13 @@ var TRACKER_PAGE_LIMIT_DEFAULT = 50;
|
|
|
9
9
|
var TRACKER_ERROR_BODY_MAX = 300;
|
|
10
10
|
var TRACKER_SECRETS_FILE = "tracker-secrets.json";
|
|
11
11
|
var TRACKER_LOCAL_AUTHOR = "owner";
|
|
12
|
+
var ISSUE_FILTERS = ["status", "labels", "assignee", "author", "search", "createdAfter", "createdBefore", "updatedAfter", "updatedBefore"];
|
|
13
|
+
var ISSUE_SORT_FIELDS = ["created", "updated", "priority", "title", "comments"];
|
|
14
|
+
var COMMENT_FILTERS = ["updatedAfter"];
|
|
15
|
+
var LEGACY_ISSUE_CAPABILITIES = {
|
|
16
|
+
list: { filters: ["status"], clientSide: ["status"], sorts: [] },
|
|
17
|
+
comments: { filters: [], clientSide: [], order: ["asc"] }
|
|
18
|
+
};
|
|
12
19
|
|
|
13
20
|
// src/tracker/tracker.lib.ts
|
|
14
21
|
var TrackerError = class extends Error {
|
|
@@ -50,6 +57,89 @@ function nameOf(table, category) {
|
|
|
50
57
|
function priorityName(table, priority) {
|
|
51
58
|
return Object.entries(table).find(([, value]) => value === priority)?.[0];
|
|
52
59
|
}
|
|
60
|
+
function usedFilters(query, names) {
|
|
61
|
+
return names.filter((name) => {
|
|
62
|
+
const value = query[name];
|
|
63
|
+
return value !== void 0 && !(Array.isArray(value) && value.length === 0);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function assertListQuery(subject, capabilities, query, names) {
|
|
67
|
+
const used = usedFilters(query, names);
|
|
68
|
+
for (const name of used) {
|
|
69
|
+
if (!capabilities.filters.includes(name)) throw new TrackerError("unsupported", `${subject}: \u0444\u0438\u043B\u044C\u0442\u0440 ${name} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
|
|
70
|
+
}
|
|
71
|
+
const sort = query.sort;
|
|
72
|
+
if (sort !== void 0 && !capabilities.sorts.includes(sort.field)) {
|
|
73
|
+
throw new TrackerError("unsupported", `${subject}: \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0430 \u043F\u043E ${sort.field} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
|
|
74
|
+
}
|
|
75
|
+
if (sort?.direction !== void 0 && sort.direction !== "asc" && sort.direction !== "desc") {
|
|
76
|
+
throw new TrackerError("invalid_request", `${subject}: \u043D\u0430\u043F\u0440\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0438 \u2014 asc \u0438\u043B\u0438 desc.`);
|
|
77
|
+
}
|
|
78
|
+
const limit = query.limit;
|
|
79
|
+
if (limit !== void 0 && !(typeof limit === "number" && Number.isSafeInteger(limit) && limit >= 1)) {
|
|
80
|
+
throw new TrackerError("invalid_request", `${subject}: limit \u2014 \u0446\u0435\u043B\u043E\u0435 \u0447\u0438\u0441\u043B\u043E \u043E\u0442 1.`);
|
|
81
|
+
}
|
|
82
|
+
for (const name of used) {
|
|
83
|
+
const value = query[name];
|
|
84
|
+
if (isTimeFilter(name)) {
|
|
85
|
+
if (typeof value !== "number" || !(Number.isSafeInteger(value) && value >= 0)) {
|
|
86
|
+
throw new TrackerError("invalid_request", `${subject}: \u0444\u0438\u043B\u044C\u0442\u0440 ${name} \u2014 \u0432\u0440\u0435\u043C\u044F \u0432 \u043C\u0441, \u0446\u0435\u043B\u043E\u0435 \u0447\u0438\u0441\u043B\u043E \u043E\u0442 0.`);
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (isArrayFilter(name)) {
|
|
91
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.trim() === "")) {
|
|
92
|
+
throw new TrackerError("invalid_request", `${subject}: \u0444\u0438\u043B\u044C\u0442\u0440 ${name} \u2014 \u0441\u043F\u0438\u0441\u043E\u043A \u043D\u0435\u043F\u0443\u0441\u0442\u044B\u0445 \u0441\u0442\u0440\u043E\u043A.`);
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (value === null) continue;
|
|
97
|
+
if (typeof value === "string") {
|
|
98
|
+
if (value.trim() === "") throw new TrackerError("invalid_request", `${subject}: \u0444\u0438\u043B\u044C\u0442\u0440 ${name} \u2014 \u043F\u0443\u0441\u0442\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430.`);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (Array.isArray(value)) {
|
|
102
|
+
if (value.some((item) => typeof item !== "string" || item.trim() === "")) {
|
|
103
|
+
throw new TrackerError("invalid_request", `${subject}: \u0444\u0438\u043B\u044C\u0442\u0440 ${name} \u2014 \u0441\u043F\u0438\u0441\u043E\u043A \u043D\u0435\u043F\u0443\u0441\u0442\u044B\u0445 \u0441\u0442\u0440\u043E\u043A.`);
|
|
104
|
+
}
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
throw new TrackerError("invalid_request", `${subject}: \u0444\u0438\u043B\u044C\u0442\u0440 ${name} \u2014 \u0441\u0442\u0440\u043E\u043A\u0430, \u0441\u043F\u0438\u0441\u043E\u043A \u0441\u0442\u0440\u043E\u043A \u0438\u043B\u0438 null.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function isTimeFilter(name) {
|
|
111
|
+
return name.endsWith("After") || name.endsWith("Before");
|
|
112
|
+
}
|
|
113
|
+
function isArrayFilter(name) {
|
|
114
|
+
return name === "status" || name === "labels";
|
|
115
|
+
}
|
|
116
|
+
function assertCommentQuery(subject, capabilities, query) {
|
|
117
|
+
assertListQuery(subject, { filters: capabilities.filters, clientSide: capabilities.clientSide, sorts: [] }, query, COMMENT_FILTERS);
|
|
118
|
+
if (query.order !== void 0 && !capabilities.order.includes(query.order)) {
|
|
119
|
+
throw new TrackerError("unsupported", `${subject}: \u043F\u043E\u0440\u044F\u0434\u043E\u043A ${query.order} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function issueCapabilitiesOf(definition) {
|
|
123
|
+
return definition.issues ?? LEGACY_ISSUE_CAPABILITIES;
|
|
124
|
+
}
|
|
125
|
+
function isoTime(ms) {
|
|
126
|
+
return new Date(ms).toISOString();
|
|
127
|
+
}
|
|
128
|
+
function inTimeRange(value, after, before) {
|
|
129
|
+
if (after === void 0 && before === void 0) return true;
|
|
130
|
+
if (value === void 0) return false;
|
|
131
|
+
return (after === void 0 || value >= after) && (before === void 0 || value <= before);
|
|
132
|
+
}
|
|
133
|
+
function containsText(texts, needle) {
|
|
134
|
+
const lower = needle.toLowerCase();
|
|
135
|
+
return texts.some((value) => value !== void 0 && value.toLowerCase().includes(lower));
|
|
136
|
+
}
|
|
137
|
+
function quoted(value) {
|
|
138
|
+
return `"${value.replace(/[\\"]/g, "\\$&")}"`;
|
|
139
|
+
}
|
|
140
|
+
function keysFor(table, values) {
|
|
141
|
+
return Object.entries(table).filter(([, value]) => values.includes(value)).map(([key]) => key);
|
|
142
|
+
}
|
|
53
143
|
|
|
54
144
|
// src/tracker/mapping.const.ts
|
|
55
145
|
var SOURCECRAFT_STATUSES = {
|
|
@@ -66,6 +156,12 @@ var SOURCECRAFT_STATUS_SLUGS = {
|
|
|
66
156
|
done: "closed",
|
|
67
157
|
declined: "declined"
|
|
68
158
|
};
|
|
159
|
+
var SOURCECRAFT_STATUS_FILTER = {
|
|
160
|
+
open: ["open", "paused"],
|
|
161
|
+
in_progress: ["in_progress"],
|
|
162
|
+
done: ["closed"],
|
|
163
|
+
declined: ["declined", "duplicate"]
|
|
164
|
+
};
|
|
69
165
|
var SOURCECRAFT_PRIORITIES = {
|
|
70
166
|
trivial: "trivial",
|
|
71
167
|
minor: "minor",
|
|
@@ -130,11 +226,7 @@ async function requestJson(url, request) {
|
|
|
130
226
|
throw new TrackerError("failed", redactSecretText(`\u0422\u0440\u0435\u043A\u0435\u0440 \u043D\u0435 \u043E\u0442\u0432\u0435\u0442\u0438\u043B (${hostOf(url)}): ${error instanceof Error ? error.message : String(error)}`));
|
|
131
227
|
}
|
|
132
228
|
const raw = await response.text();
|
|
133
|
-
if (!response.ok)
|
|
134
|
-
if (response.status === 404 && request.missingIsIssue === true) throw new TrackerError("unknown_issue", "\u0417\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430 \u0432 \u0442\u0440\u0435\u043A\u0435\u0440\u0435.");
|
|
135
|
-
const detail = redactSecretText(raw.replace(/\s+/g, " ").trim()).slice(0, TRACKER_ERROR_BODY_MAX);
|
|
136
|
-
throw new TrackerError("failed", `\u0422\u0440\u0435\u043A\u0435\u0440 ${hostOf(url)} \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${String(response.status)}${detail === "" ? "" : `: ${detail}`}.`);
|
|
137
|
-
}
|
|
229
|
+
if (!response.ok) throw failureOf(url, response, raw, request);
|
|
138
230
|
if (raw === "") return { body: void 0, headers: response.headers };
|
|
139
231
|
try {
|
|
140
232
|
return { body: JSON.parse(raw), headers: response.headers };
|
|
@@ -142,6 +234,38 @@ async function requestJson(url, request) {
|
|
|
142
234
|
throw new TrackerError("failed", `\u0422\u0440\u0435\u043A\u0435\u0440 ${hostOf(url)} \u0432\u0435\u0440\u043D\u0443\u043B \u043D\u0435 JSON.`);
|
|
143
235
|
}
|
|
144
236
|
}
|
|
237
|
+
function failureOf(url, response, raw, request) {
|
|
238
|
+
const status = response.status;
|
|
239
|
+
if (status === 404 && request.missing !== void 0) return new TrackerError(request.missing.failure, request.missing.message);
|
|
240
|
+
if (status === 404 && request.missingIsIssue === true) return new TrackerError("unknown_issue", "\u0417\u0430\u0434\u0430\u0447\u0430 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u0430 \u0432 \u0442\u0440\u0435\u043A\u0435\u0440\u0435.");
|
|
241
|
+
const detail = redactSecretText(raw.replace(/\s+/g, " ").trim()).slice(0, TRACKER_ERROR_BODY_MAX);
|
|
242
|
+
const answered = `\u0422\u0440\u0435\u043A\u0435\u0440 ${hostOf(url)} \u043E\u0442\u0432\u0435\u0442\u0438\u043B ${String(status)}${detail === "" ? "" : `: ${detail}`}.`;
|
|
243
|
+
if (request.conflictOn?.includes(status) === true) return new TrackerError("conflict", answered);
|
|
244
|
+
const secondaryLimit = status === 403 && (response.headers.get("x-ratelimit-remaining")?.trim() === "0" || retryAfterOf(response.headers.get("retry-after")?.trim(), Date.now()) !== void 0);
|
|
245
|
+
if (status === 429 || secondaryLimit) {
|
|
246
|
+
const delay = retryDelayOf(response.headers);
|
|
247
|
+
return new TrackerError("rate_limited", `\u0422\u0440\u0435\u043A\u0435\u0440 ${hostOf(url)} \u043E\u0433\u0440\u0430\u043D\u0438\u0447\u0438\u043B \u0447\u0430\u0441\u0442\u043E\u0442\u0443 \u0437\u0430\u043F\u0440\u043E\u0441\u043E\u0432${delay === void 0 ? "" : `, \u043F\u043E\u0432\u0442\u043E\u0440 \u0447\u0435\u0440\u0435\u0437 ${String(delay)} \u0441`}.`);
|
|
248
|
+
}
|
|
249
|
+
return new TrackerError("failed", answered);
|
|
250
|
+
}
|
|
251
|
+
function retryDelayOf(headers, now = Date.now()) {
|
|
252
|
+
const retryAfter = retryAfterOf(headers.get("retry-after")?.trim(), now);
|
|
253
|
+
if (retryAfter !== void 0) return retryAfter;
|
|
254
|
+
for (const name of ["x-ratelimit-reset", "ratelimit-reset"]) {
|
|
255
|
+
const reset = headers.get(name)?.trim();
|
|
256
|
+
if (reset !== void 0 && /^\d+$/.test(reset)) return resetDelay(Number(reset), now);
|
|
257
|
+
}
|
|
258
|
+
return void 0;
|
|
259
|
+
}
|
|
260
|
+
function retryAfterOf(value, now) {
|
|
261
|
+
if (value === void 0 || value === "") return void 0;
|
|
262
|
+
if (/^\d+$/.test(value)) return Number(value);
|
|
263
|
+
const at = Date.parse(value);
|
|
264
|
+
return Number.isFinite(at) ? Math.max(0, Math.ceil((at - now) / 1e3)) : void 0;
|
|
265
|
+
}
|
|
266
|
+
function resetDelay(reset, now) {
|
|
267
|
+
return reset >= 1e9 ? Math.max(0, reset - Math.floor(now / 1e3)) : reset;
|
|
268
|
+
}
|
|
145
269
|
function hostOf(url) {
|
|
146
270
|
try {
|
|
147
271
|
return new URL(url).host;
|
|
@@ -179,7 +303,13 @@ function listTrackers() {
|
|
|
179
303
|
var DEFAULT_API = "https://api.github.com";
|
|
180
304
|
var TARGET = /^[\w.-]+\/[\w.-]+$/;
|
|
181
305
|
var NUMBER = /^[1-9]\d{0,14}$/;
|
|
306
|
+
var LIST_SUBJECT = "\u0417\u0430\u0434\u0430\u0447\u0438 GitHub";
|
|
307
|
+
var COMMENTS_SUBJECT = "\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitHub";
|
|
182
308
|
function githubTracker() {
|
|
309
|
+
const issues = {
|
|
310
|
+
list: { filters: ISSUE_FILTERS, clientSide: ["status"], sorts: ["created", "updated", "comments"] },
|
|
311
|
+
comments: { filters: ["updatedAfter"], clientSide: [], order: ["asc"] }
|
|
312
|
+
};
|
|
183
313
|
return {
|
|
184
314
|
descriptor: {
|
|
185
315
|
tracker: "github",
|
|
@@ -191,13 +321,15 @@ function githubTracker() {
|
|
|
191
321
|
],
|
|
192
322
|
features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: true, statuses: ["open", "in_progress", "done", "declined"] }
|
|
193
323
|
},
|
|
194
|
-
create: (context) => createGithubAdapter(context)
|
|
324
|
+
create: (context) => createGithubAdapter(context, issues),
|
|
325
|
+
issues
|
|
195
326
|
};
|
|
196
327
|
}
|
|
197
|
-
function createGithubAdapter({ connection, fetch }) {
|
|
328
|
+
function createGithubAdapter({ connection, fetch }, capabilities) {
|
|
198
329
|
const target = requireTarget(connection.target, "owner/repo \u0440\u0435\u043F\u043E\u0437\u0438\u0442\u043E\u0440\u0438\u044F GitHub");
|
|
199
330
|
if (!TARGET.test(target)) throw new TrackerError("invalid_request", "target GitHub \u2014 owner/repo.");
|
|
200
|
-
const
|
|
331
|
+
const api = baseUrl(connection.url, DEFAULT_API);
|
|
332
|
+
const root = `${api}/repos/${target}/issues`;
|
|
201
333
|
const headers = {
|
|
202
334
|
accept: "application/vnd.github+json",
|
|
203
335
|
"x-github-api-version": "2022-11-28",
|
|
@@ -213,14 +345,13 @@ function createGithubAdapter({ connection, fetch }) {
|
|
|
213
345
|
};
|
|
214
346
|
return {
|
|
215
347
|
list: async (query) => {
|
|
348
|
+
assertListQuery(LIST_SUBJECT, capabilities.list, query, ISSUE_FILTERS);
|
|
349
|
+
assertCsvLabels(query.labels);
|
|
216
350
|
const page = pageOf(query.cursor);
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
return
|
|
221
|
-
items: query.status === void 0 ? items : items.filter((issue) => query.status?.includes(issue.status.category) === true),
|
|
222
|
-
.../rel="next"/.test(responseHeaders.get("link") ?? "") ? { nextCursor: String(page + 1) } : {}
|
|
223
|
-
};
|
|
351
|
+
const search = needsSearch(query);
|
|
352
|
+
const params = search ? searchParams(target, query, page) : issuesParams(query, page);
|
|
353
|
+
const { body, headers: responseHeaders } = await requestJson(`${search ? `${api}/search/issues` : root}?${params.toString()}`, { fetch, headers });
|
|
354
|
+
return issuePageOf(rowsOf(body, search), query, responseHeaders.get("link") ?? "", page);
|
|
224
355
|
},
|
|
225
356
|
get: async (key) => issueOf(await rawOf(key)),
|
|
226
357
|
create: async (input) => {
|
|
@@ -239,8 +370,10 @@ function createGithubAdapter({ connection, fetch }) {
|
|
|
239
370
|
return issueOf(response.body);
|
|
240
371
|
},
|
|
241
372
|
comments: async (key, query) => {
|
|
373
|
+
assertCommentQuery(COMMENTS_SUBJECT, capabilities.comments, query);
|
|
242
374
|
const page = pageOf(query.cursor);
|
|
243
375
|
const params = new URLSearchParams({ per_page: String(query.limit), page: String(page) });
|
|
376
|
+
if (query.updatedAfter !== void 0) params.set("since", isoTime(query.updatedAfter));
|
|
244
377
|
const { body, headers: responseHeaders } = await requestJson(`${root}/${number(key)}/comments?${params.toString()}`, { fetch, headers, missingIsIssue: true });
|
|
245
378
|
return { items: (Array.isArray(body) ? body : []).map(commentOf), .../rel="next"/.test(responseHeaders.get("link") ?? "") ? { nextCursor: String(page + 1) } : {} };
|
|
246
379
|
},
|
|
@@ -257,6 +390,68 @@ function stateFilter(status) {
|
|
|
257
390
|
if (status.every((item) => item === "done" || item === "declined")) return "closed";
|
|
258
391
|
return "all";
|
|
259
392
|
}
|
|
393
|
+
function needsSearch(query) {
|
|
394
|
+
return query.search !== void 0 || query.createdAfter !== void 0 || query.createdBefore !== void 0 || query.updatedBefore !== void 0;
|
|
395
|
+
}
|
|
396
|
+
function assertCsvLabels(labels) {
|
|
397
|
+
if (labels?.some((label) => label.includes(",")) === true) throw new TrackerError("invalid_request", `${LIST_SUBJECT}: \u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u0434\u043E\u043B\u0436\u043D\u0430 \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0437\u0430\u043F\u044F\u0442\u0443\u044E.`);
|
|
398
|
+
}
|
|
399
|
+
function issuesParams(query, page) {
|
|
400
|
+
const params = new URLSearchParams({ state: stateFilter(query.status), per_page: String(query.limit), page: String(page) });
|
|
401
|
+
if (query.labels !== void 0 && query.labels.length > 0) params.set("labels", query.labels.join(","));
|
|
402
|
+
if (query.assignee !== void 0) params.set("assignee", query.assignee ?? "none");
|
|
403
|
+
if (query.author !== void 0) params.set("creator", query.author);
|
|
404
|
+
if (query.updatedAfter !== void 0) params.set("since", isoTime(query.updatedAfter));
|
|
405
|
+
if (query.sort !== void 0) {
|
|
406
|
+
params.set("sort", query.sort.field);
|
|
407
|
+
params.set("direction", query.sort.direction ?? "desc");
|
|
408
|
+
}
|
|
409
|
+
return params;
|
|
410
|
+
}
|
|
411
|
+
function searchParams(target, query, page) {
|
|
412
|
+
const params = new URLSearchParams({ q: searchQuery(target, query), per_page: String(query.limit), page: String(page) });
|
|
413
|
+
const sort = query.sort;
|
|
414
|
+
params.set("sort", sort?.field ?? "created");
|
|
415
|
+
params.set("order", sort?.direction ?? "desc");
|
|
416
|
+
return params;
|
|
417
|
+
}
|
|
418
|
+
function searchQuery(target, query) {
|
|
419
|
+
const parts = [`repo:${target}`, "is:issue"];
|
|
420
|
+
const state = searchStateFilter(query.status);
|
|
421
|
+
if (state !== void 0) parts.push(state);
|
|
422
|
+
for (const label of query.labels ?? []) parts.push(`label:${quoted(label)}`);
|
|
423
|
+
if (query.assignee !== void 0) parts.push(query.assignee === null ? "no:assignee" : `assignee:${quoted(query.assignee)}`);
|
|
424
|
+
if (query.author !== void 0) parts.push(`author:${quoted(query.author)}`);
|
|
425
|
+
const created = timeRangeQualifier("created", query.createdAfter, query.createdBefore);
|
|
426
|
+
if (created !== void 0) parts.push(created);
|
|
427
|
+
const updated = timeRangeQualifier("updated", query.updatedAfter, query.updatedBefore);
|
|
428
|
+
if (updated !== void 0) parts.push(updated);
|
|
429
|
+
if (query.search !== void 0) parts.push(quoted(query.search));
|
|
430
|
+
return parts.join(" ");
|
|
431
|
+
}
|
|
432
|
+
function timeRangeQualifier(field, after, before) {
|
|
433
|
+
if (after !== void 0 && before !== void 0) return `${field}:${isoTime(after)}..${isoTime(before)}`;
|
|
434
|
+
if (after !== void 0) return `${field}:>=${isoTime(after)}`;
|
|
435
|
+
if (before !== void 0) return `${field}:<=${isoTime(before)}`;
|
|
436
|
+
return void 0;
|
|
437
|
+
}
|
|
438
|
+
function rowsOf(body, search) {
|
|
439
|
+
if (!search) return Array.isArray(body) ? body : [];
|
|
440
|
+
return isRecord(body) && Array.isArray(body.items) ? body.items : [];
|
|
441
|
+
}
|
|
442
|
+
function searchStateFilter(status) {
|
|
443
|
+
if (status === void 0) return void 0;
|
|
444
|
+
if (status.every((item) => item === "open" || item === "in_progress")) return "is:open";
|
|
445
|
+
if (status.every((item) => item === "done" || item === "declined")) return "is:closed";
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
function issuePageOf(rows, query, link, page) {
|
|
449
|
+
const items = rows.filter((row) => isRecord(row) && row.pull_request === void 0).map(issueOf);
|
|
450
|
+
return {
|
|
451
|
+
items: query.status === void 0 ? items : items.filter((issue) => query.status?.includes(issue.status.category) === true),
|
|
452
|
+
.../rel="next"/.test(link) ? { nextCursor: String(page + 1) } : {}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
260
455
|
function composeLabels(plain, priority, status) {
|
|
261
456
|
const priorityLabel = priority === void 0 ? void 0 : priorityName(GITHUB_PRIORITY_LABELS, priority);
|
|
262
457
|
return [...plain, ...priorityLabel === void 0 ? [] : [priorityLabel], ...status === "in_progress" ? [GITHUB_IN_PROGRESS_LABEL] : []];
|
|
@@ -308,6 +503,16 @@ function commentOf(raw) {
|
|
|
308
503
|
var DEFAULT_URL = "https://gitlab.com";
|
|
309
504
|
var TARGET2 = /^(\d{1,15}|[\w.-]+(\/[\w.-]+)+)$/;
|
|
310
505
|
var IID = /^[1-9]\d{0,14}$/;
|
|
506
|
+
var ISSUE_CAPABILITIES = {
|
|
507
|
+
list: { filters: ISSUE_FILTERS, clientSide: ["status"], sorts: ["created", "updated", "priority", "title"] },
|
|
508
|
+
comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc", "desc"] }
|
|
509
|
+
};
|
|
510
|
+
var ORDER_BY = {
|
|
511
|
+
created: "created_at",
|
|
512
|
+
updated: "updated_at",
|
|
513
|
+
priority: "priority",
|
|
514
|
+
title: "title"
|
|
515
|
+
};
|
|
311
516
|
function gitlabTracker() {
|
|
312
517
|
return {
|
|
313
518
|
descriptor: {
|
|
@@ -320,7 +525,8 @@ function gitlabTracker() {
|
|
|
320
525
|
],
|
|
321
526
|
features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: false, statuses: ["open", "in_progress", "done"] }
|
|
322
527
|
},
|
|
323
|
-
create: (context) => createGitlabAdapter(context)
|
|
528
|
+
create: (context) => createGitlabAdapter(context),
|
|
529
|
+
issues: ISSUE_CAPABILITIES
|
|
324
530
|
};
|
|
325
531
|
}
|
|
326
532
|
function createGitlabAdapter({ connection, fetch }) {
|
|
@@ -335,8 +541,26 @@ function createGitlabAdapter({ connection, fetch }) {
|
|
|
335
541
|
const currentOf = async (key) => issueOf2((await requestJson(`${root}/${iid(key)}`, { fetch, headers, missingIsIssue: true })).body);
|
|
336
542
|
return {
|
|
337
543
|
list: async (query) => {
|
|
544
|
+
assertListQuery("\u0417\u0430\u0434\u0430\u0447\u0438 GitLab", ISSUE_CAPABILITIES.list, query, ISSUE_FILTERS);
|
|
338
545
|
const page = pageOf2(query.cursor);
|
|
339
|
-
const params = new URLSearchParams({
|
|
546
|
+
const params = new URLSearchParams({
|
|
547
|
+
state: stateFilter2(query.status),
|
|
548
|
+
per_page: String(query.limit),
|
|
549
|
+
page: String(page),
|
|
550
|
+
order_by: query.sort === void 0 ? "created_at" : ORDER_BY[query.sort.field] ?? "created_at",
|
|
551
|
+
sort: query.sort?.direction ?? "desc"
|
|
552
|
+
});
|
|
553
|
+
if (query.labels !== void 0 && query.labels.length > 0) {
|
|
554
|
+
if (query.labels.some((label) => label.includes(","))) throw new TrackerError("invalid_request", "\u0417\u0430\u0434\u0430\u0447\u0438 GitLab: \u043C\u0435\u0442\u043A\u0430 \u043D\u0435 \u0434\u043E\u043B\u0436\u043D\u0430 \u0441\u043E\u0434\u0435\u0440\u0436\u0430\u0442\u044C \u0437\u0430\u043F\u044F\u0442\u0443\u044E.");
|
|
555
|
+
params.set("labels", query.labels.join(","));
|
|
556
|
+
}
|
|
557
|
+
if (query.assignee !== void 0) params.set(query.assignee === null ? "assignee_id" : "assignee_username", query.assignee === null ? "None" : query.assignee);
|
|
558
|
+
if (query.author !== void 0) params.set("author_username", query.author);
|
|
559
|
+
if (query.search !== void 0) params.set("search", query.search);
|
|
560
|
+
if (query.createdAfter !== void 0) params.set("created_after", isoTime(query.createdAfter));
|
|
561
|
+
if (query.createdBefore !== void 0) params.set("created_before", isoTime(query.createdBefore));
|
|
562
|
+
if (query.updatedAfter !== void 0) params.set("updated_after", isoTime(query.updatedAfter));
|
|
563
|
+
if (query.updatedBefore !== void 0) params.set("updated_before", isoTime(query.updatedBefore));
|
|
340
564
|
const { body, headers: responseHeaders } = await requestJson(`${root}?${params.toString()}`, { fetch, headers });
|
|
341
565
|
const items = (Array.isArray(body) ? body : []).map(issueOf2);
|
|
342
566
|
const next = responseHeaders.get("x-next-page");
|
|
@@ -356,11 +580,12 @@ function createGitlabAdapter({ connection, fetch }) {
|
|
|
356
580
|
return issueOf2((await requestJson(`${root}/${iid(key)}`, { fetch, headers, method: "PUT", body: patchBody2(patch, current), missingIsIssue: true })).body);
|
|
357
581
|
},
|
|
358
582
|
comments: async (key, query) => {
|
|
583
|
+
assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 GitLab", ISSUE_CAPABILITIES.comments, query);
|
|
359
584
|
const page = pageOf2(query.cursor);
|
|
360
|
-
const params = new URLSearchParams({ sort: "asc", order_by: "created_at", per_page: String(query.limit), page: String(page) });
|
|
585
|
+
const params = new URLSearchParams({ sort: query.order ?? "asc", order_by: "created_at", per_page: String(query.limit), page: String(page) });
|
|
361
586
|
const { body, headers: responseHeaders } = await requestJson(`${root}/${iid(key)}/notes?${params.toString()}`, { fetch, headers, missingIsIssue: true });
|
|
362
587
|
const next = responseHeaders.get("x-next-page");
|
|
363
|
-
const items = (Array.isArray(body) ? body : []).filter((note) => isRecord(note) && note.system !== true).map(commentOf2);
|
|
588
|
+
const items = (Array.isArray(body) ? body : []).filter((note) => isRecord(note) && note.system !== true && inTimeRange(toTimestamp(note.updated_at), query.updatedAfter, void 0)).map(commentOf2);
|
|
364
589
|
return { items, ...next === null || next === "" ? {} : { nextCursor: next } };
|
|
365
590
|
},
|
|
366
591
|
comment: async (key, body) => commentOf2((await requestJson(`${root}/${iid(key)}/notes`, { fetch, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
|
|
@@ -430,6 +655,11 @@ var PROJECT_KEY = /^[A-Z][A-Z0-9_]{0,49}$/;
|
|
|
430
655
|
var ISSUE_KEY = /^[A-Z][A-Z0-9_]{0,49}-[1-9]\d{0,14}$/;
|
|
431
656
|
var FIELDS = "summary,description,status,priority,labels,assignee,created,updated";
|
|
432
657
|
var CATEGORY_JQL = { open: '"To Do"', in_progress: '"In Progress"', done: "Done" };
|
|
658
|
+
var ISSUE_CAPABILITIES2 = {
|
|
659
|
+
list: { filters: ISSUE_FILTERS, clientSide: [], sorts: ["created", "updated", "priority", "title"] },
|
|
660
|
+
comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc", "desc"] }
|
|
661
|
+
};
|
|
662
|
+
var JIRA_SORT_FIELDS = { created: "created", updated: "updated", priority: "priority", title: "summary" };
|
|
433
663
|
function jiraTracker() {
|
|
434
664
|
return {
|
|
435
665
|
descriptor: {
|
|
@@ -443,7 +673,8 @@ function jiraTracker() {
|
|
|
443
673
|
],
|
|
444
674
|
features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: false, statuses: ["open", "in_progress", "done"] }
|
|
445
675
|
},
|
|
446
|
-
create: (context) => createJiraAdapter(context)
|
|
676
|
+
create: (context) => createJiraAdapter(context),
|
|
677
|
+
issues: ISSUE_CAPABILITIES2
|
|
447
678
|
};
|
|
448
679
|
}
|
|
449
680
|
function createJiraAdapter({ connection, fetch }) {
|
|
@@ -462,7 +693,8 @@ function createJiraAdapter({ connection, fetch }) {
|
|
|
462
693
|
const load = async (value) => toIssue2((await requestJson(`${api}/issue/${key(value)}?fields=${FIELDS}`, { fetch, headers, missingIsIssue: true })).body);
|
|
463
694
|
return {
|
|
464
695
|
list: async (query) => {
|
|
465
|
-
|
|
696
|
+
assertListQuery("\u0417\u0430\u0434\u0430\u0447\u0438 Jira", ISSUE_CAPABILITIES2.list, query, ISSUE_FILTERS);
|
|
697
|
+
const jql = listJql(project, query);
|
|
466
698
|
if (jql === void 0) return { items: [] };
|
|
467
699
|
if (query.cursor?.startsWith("s:") !== true) {
|
|
468
700
|
const params2 = new URLSearchParams({ jql, fields: FIELDS, maxResults: String(query.limit) });
|
|
@@ -507,23 +739,44 @@ function createJiraAdapter({ connection, fetch }) {
|
|
|
507
739
|
return load(value);
|
|
508
740
|
},
|
|
509
741
|
comments: async (value, query) => {
|
|
742
|
+
assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 Jira", ISSUE_CAPABILITIES2.comments, query);
|
|
510
743
|
const startAt = query.cursor === void 0 ? 0 : Number(query.cursor);
|
|
511
|
-
const params = new URLSearchParams({ startAt: String(Number.isInteger(startAt) && startAt >= 0 ? startAt : 0), maxResults: String(query.limit), orderBy: "created" });
|
|
744
|
+
const params = new URLSearchParams({ startAt: String(Number.isInteger(startAt) && startAt >= 0 ? startAt : 0), maxResults: String(query.limit), orderBy: query.order === "desc" ? "-created" : "created" });
|
|
512
745
|
const { body } = await requestJson(`${api}/issue/${key(value)}/comment?${params.toString()}`, { fetch, headers, missingIsIssue: true });
|
|
513
746
|
const record = isRecord(body) ? body : {};
|
|
514
|
-
const
|
|
747
|
+
const raw = Array.isArray(record.comments) ? record.comments : [];
|
|
748
|
+
const items = raw.filter((item) => inTimeRange(toTimestamp(isRecord(item) ? item.updated : void 0), query.updatedAfter, void 0));
|
|
515
749
|
const total = typeof record.total === "number" ? record.total : 0;
|
|
516
750
|
const at = typeof record.startAt === "number" ? record.startAt : 0;
|
|
517
|
-
return { items: items.map(commentOf3), ...at +
|
|
751
|
+
return { items: items.map(commentOf3), ...at + raw.length < total && raw.length > 0 ? { nextCursor: String(at + raw.length) } : {} };
|
|
518
752
|
},
|
|
519
753
|
comment: async (value, body) => commentOf3((await requestJson(`${api}/issue/${key(value)}/comment`, { fetch, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
|
|
520
754
|
};
|
|
521
755
|
}
|
|
522
|
-
function listJql(project,
|
|
523
|
-
const
|
|
524
|
-
if (status
|
|
525
|
-
|
|
526
|
-
|
|
756
|
+
function listJql(project, query) {
|
|
757
|
+
const clauses = [];
|
|
758
|
+
if (query.status !== void 0) {
|
|
759
|
+
const names = query.status.flatMap((item) => CATEGORY_JQL[item] ?? []);
|
|
760
|
+
if (names.length === 0) return void 0;
|
|
761
|
+
clauses.push(`statusCategory in (${names.join(", ")})`);
|
|
762
|
+
}
|
|
763
|
+
for (const label of query.labels ?? []) clauses.push(`labels = ${quoted(label.replace(/\s+/g, "-"))}`);
|
|
764
|
+
if (query.assignee !== void 0) clauses.push(query.assignee === null ? "assignee is EMPTY" : `assignee = ${quoted(query.assignee)}`);
|
|
765
|
+
if (query.author !== void 0) clauses.push(`reporter = ${quoted(query.author)}`);
|
|
766
|
+
if (query.search !== void 0) clauses.push(`text ~ ${quoted(query.search)}`);
|
|
767
|
+
if (query.createdAfter !== void 0) clauses.push(`created >= ${quoted(jiraTime(query.createdAfter, "down"))}`);
|
|
768
|
+
if (query.createdBefore !== void 0) clauses.push(`created <= ${quoted(jiraTime(query.createdBefore, "up"))}`);
|
|
769
|
+
if (query.updatedAfter !== void 0) clauses.push(`updated >= ${quoted(jiraTime(query.updatedAfter, "down"))}`);
|
|
770
|
+
if (query.updatedBefore !== void 0) clauses.push(`updated <= ${quoted(jiraTime(query.updatedBefore, "up"))}`);
|
|
771
|
+
const sort = query.sort;
|
|
772
|
+
const field = sort === void 0 ? "created" : sort.field;
|
|
773
|
+
const direction = sort?.direction ?? "desc";
|
|
774
|
+
return `${[`project = ${quoted(project)}`, ...clauses].join(" AND ")} ORDER BY ${JIRA_SORT_FIELDS[field]} ${direction.toUpperCase()}`;
|
|
775
|
+
}
|
|
776
|
+
function jiraTime(ms, outward) {
|
|
777
|
+
const at = new Date(outward === "down" ? Math.floor(ms / 6e4) * 6e4 : Math.ceil(ms / 6e4) * 6e4);
|
|
778
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
779
|
+
return `${String(at.getUTCFullYear())}/${pad(at.getUTCMonth() + 1)}/${pad(at.getUTCDate())} ${pad(at.getUTCHours())}:${pad(at.getUTCMinutes())}`;
|
|
527
780
|
}
|
|
528
781
|
function fieldsOf(patch) {
|
|
529
782
|
return {
|
|
@@ -578,6 +831,14 @@ function commentOf3(raw) {
|
|
|
578
831
|
|
|
579
832
|
// src/tracker/local.lib.ts
|
|
580
833
|
var STATUS_NAMES = { open: "Open", in_progress: "In progress", done: "Done", declined: "Declined" };
|
|
834
|
+
var ISSUE_CAPABILITIES3 = {
|
|
835
|
+
list: {
|
|
836
|
+
filters: ["status", "labels", "assignee", "search", "createdAfter", "createdBefore", "updatedAfter", "updatedBefore"],
|
|
837
|
+
clientSide: ["labels", "assignee", "search", "createdAfter", "createdBefore", "updatedAfter", "updatedBefore"],
|
|
838
|
+
sorts: []
|
|
839
|
+
},
|
|
840
|
+
comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc"] }
|
|
841
|
+
};
|
|
581
842
|
function localTracker() {
|
|
582
843
|
return {
|
|
583
844
|
descriptor: {
|
|
@@ -586,6 +847,7 @@ function localTracker() {
|
|
|
586
847
|
fields: [],
|
|
587
848
|
features: { create: true, update: true, comment: true, priority: true, labels: true, assignee: true, statuses: [...ALL_STATUSES] }
|
|
588
849
|
},
|
|
850
|
+
issues: ISSUE_CAPABILITIES3,
|
|
589
851
|
create: (context) => createLocalAdapter(context)
|
|
590
852
|
};
|
|
591
853
|
}
|
|
@@ -600,9 +862,11 @@ function createLocalAdapter({ store, projectId, author }) {
|
|
|
600
862
|
return row;
|
|
601
863
|
};
|
|
602
864
|
return {
|
|
603
|
-
list:
|
|
865
|
+
// list и comments async: отказ проверки запроса — rejected promise, как у остальных адаптеров.
|
|
866
|
+
list: async (query) => {
|
|
867
|
+
assertListQuery("\u0417\u0430\u0434\u0430\u0447\u0438 Local", ISSUE_CAPABILITIES3.list, query, ISSUE_FILTERS);
|
|
604
868
|
const page = store.listLocalIssues(projectId, { limit: query.limit, ...query.cursor === void 0 ? {} : { cursor: query.cursor }, ...query.status === void 0 ? {} : { status: query.status } });
|
|
605
|
-
return
|
|
869
|
+
return { items: page.items.filter((row) => matchesIssueQuery(row, query)).map(toIssue), ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor } };
|
|
606
870
|
},
|
|
607
871
|
get: (key) => Promise.resolve(toIssue(mustGet(key))),
|
|
608
872
|
create: (input) => Promise.resolve(toIssue(store.createLocalIssue(projectId, input))),
|
|
@@ -611,13 +875,22 @@ function createLocalAdapter({ store, projectId, author }) {
|
|
|
611
875
|
if (row === void 0) throw new TrackerError("unknown_issue", `\u0412 \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u043C \u0442\u0440\u0435\u043A\u0435\u0440\u0435 \u043D\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0438 ${key}.`);
|
|
612
876
|
return Promise.resolve(toIssue(row));
|
|
613
877
|
},
|
|
614
|
-
comments: (key, query) => {
|
|
878
|
+
comments: async (key, query) => {
|
|
879
|
+
assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 Local", ISSUE_CAPABILITIES3.comments, query);
|
|
615
880
|
const page = store.listLocalComments(projectId, mustGet(key).number, { limit: query.limit, ...query.cursor === void 0 ? {} : { cursor: query.cursor } });
|
|
616
|
-
|
|
881
|
+
const items = query.updatedAfter === void 0 ? page.items : page.items.filter((row) => inTimeRange(row.createdAt, query.updatedAfter, void 0));
|
|
882
|
+
return { items: items.map(toComment), ...page.nextCursor === void 0 ? {} : { nextCursor: page.nextCursor } };
|
|
617
883
|
},
|
|
618
884
|
comment: (key, body) => Promise.resolve(toComment(store.addLocalComment(projectId, mustGet(key).number, body, author)))
|
|
619
885
|
};
|
|
620
886
|
}
|
|
887
|
+
function matchesIssueQuery(row, query) {
|
|
888
|
+
if (query.labels !== void 0 && !query.labels.every((label) => row.labels.includes(label))) return false;
|
|
889
|
+
const assignee = query.assignee;
|
|
890
|
+
if (assignee !== void 0 && (assignee === null ? row.assignee !== void 0 : row.assignee !== assignee)) return false;
|
|
891
|
+
if (query.search !== void 0 && !containsText([row.title, row.description], query.search)) return false;
|
|
892
|
+
return inTimeRange(row.createdAt, query.createdAfter, query.createdBefore) && inTimeRange(row.updatedAt, query.updatedAfter, query.updatedBefore);
|
|
893
|
+
}
|
|
621
894
|
function toIssue(row) {
|
|
622
895
|
return {
|
|
623
896
|
key: String(row.number),
|
|
@@ -641,6 +914,16 @@ var DEFAULT_API2 = "https://api.sourcecraft.tech";
|
|
|
641
914
|
var DEFAULT_WEB = "https://sourcecraft.dev";
|
|
642
915
|
var TARGET3 = /^[\w.-]+\/[\w.-]+$/;
|
|
643
916
|
var SLUG = /^[\w-]{1,100}$/;
|
|
917
|
+
var ISSUE_SORT_FIELDS_UPSTREAM = {
|
|
918
|
+
created: "created_at",
|
|
919
|
+
updated: "updated_at",
|
|
920
|
+
priority: "priority",
|
|
921
|
+
title: "title"
|
|
922
|
+
};
|
|
923
|
+
var ISSUE_CAPABILITIES4 = {
|
|
924
|
+
list: { filters: ISSUE_FILTERS, clientSide: ["assignee"], sorts: ["created", "updated", "priority", "title"] },
|
|
925
|
+
comments: { filters: COMMENT_FILTERS, clientSide: COMMENT_FILTERS, order: ["asc", "desc"] }
|
|
926
|
+
};
|
|
644
927
|
function sourcecraftTracker() {
|
|
645
928
|
return {
|
|
646
929
|
descriptor: {
|
|
@@ -653,6 +936,7 @@ function sourcecraftTracker() {
|
|
|
653
936
|
],
|
|
654
937
|
features: { create: true, update: true, comment: true, priority: true, labels: false, assignee: false, statuses: [...ALL_STATUSES] }
|
|
655
938
|
},
|
|
939
|
+
issues: ISSUE_CAPABILITIES4,
|
|
656
940
|
create: (context) => createSourceCraftAdapter(context)
|
|
657
941
|
};
|
|
658
942
|
}
|
|
@@ -670,12 +954,18 @@ function createSourceCraftAdapter({ connection, fetch }) {
|
|
|
670
954
|
const toIssue2 = (raw) => issueOf4(raw, web);
|
|
671
955
|
return {
|
|
672
956
|
list: async (query) => {
|
|
957
|
+
assertListQuery("\u0417\u0430\u0434\u0430\u0447\u0438 SourceCraft", ISSUE_CAPABILITIES4.list, query, ISSUE_FILTERS);
|
|
673
958
|
const params = new URLSearchParams({ page_size: String(query.limit) });
|
|
674
959
|
if (query.cursor !== void 0) params.set("page_token", query.cursor);
|
|
960
|
+
const filter = issueFilterOf(query);
|
|
961
|
+
if (filter !== void 0) params.set("filter", filter);
|
|
962
|
+
if (query.sort !== void 0) params.set("sort_by", `${query.sort.direction === "asc" ? "" : "-"}${issueSortFieldOf(query.sort.field)}`);
|
|
675
963
|
const { body } = await requestJson(`${root}?${params.toString()}`, { fetch, headers });
|
|
676
964
|
const record = isRecord(body) ? body : {};
|
|
677
|
-
|
|
678
|
-
|
|
965
|
+
let issues = (Array.isArray(record.issues) ? record.issues : []).map(toIssue2);
|
|
966
|
+
if (query.status !== void 0) issues = issues.filter((issue) => query.status?.includes(issue.status.category) === true);
|
|
967
|
+
if (query.assignee === null) issues = issues.filter((issue) => issue.assignee === void 0);
|
|
968
|
+
return { items: issues, ...nextOf(record.next_page_token) };
|
|
679
969
|
},
|
|
680
970
|
get: async (key) => toIssue2((await requestJson(`${root}/${slug(key)}`, { fetch, headers, missingIsIssue: true })).body),
|
|
681
971
|
create: async (input) => {
|
|
@@ -684,11 +974,15 @@ function createSourceCraftAdapter({ connection, fetch }) {
|
|
|
684
974
|
},
|
|
685
975
|
update: async (key, patch) => toIssue2((await requestJson(`${root}/${slug(key)}`, { fetch, headers, method: "PATCH", body: patchBody3(patch), missingIsIssue: true })).body),
|
|
686
976
|
comments: async (key, query) => {
|
|
977
|
+
assertCommentQuery("\u041A\u043E\u043C\u043C\u0435\u043D\u0442\u0430\u0440\u0438\u0438 SourceCraft", ISSUE_CAPABILITIES4.comments, query);
|
|
687
978
|
const params = new URLSearchParams({ page_size: String(query.limit) });
|
|
688
979
|
if (query.cursor !== void 0) params.set("page_token", query.cursor);
|
|
980
|
+
if (query.order !== void 0) params.set("sort_by", query.order === "asc" ? "created_at" : "-created_at");
|
|
689
981
|
const { body } = await requestJson(`${root}/${slug(key)}/comments?${params.toString()}`, { fetch, headers, missingIsIssue: true });
|
|
690
982
|
const record = isRecord(body) ? body : {};
|
|
691
|
-
|
|
983
|
+
const rawComments = Array.isArray(record.issue_comments) ? record.issue_comments : [];
|
|
984
|
+
const filtered = query.updatedAfter === void 0 ? rawComments : rawComments.filter((raw) => inTimeRange(toTimestamp(isRecord(raw) ? raw.updated_at : void 0), query.updatedAfter, void 0));
|
|
985
|
+
return { items: filtered.map(commentOf4), ...nextOf(record.next_page_token) };
|
|
692
986
|
},
|
|
693
987
|
comment: async (key, body) => commentOf4((await requestJson(`${root}/${slug(key)}/comments`, { fetch, headers, method: "POST", body: { body }, missingIsIssue: true })).body)
|
|
694
988
|
};
|
|
@@ -705,6 +999,27 @@ function nextOf(token) {
|
|
|
705
999
|
const value = text(token);
|
|
706
1000
|
return value === void 0 ? {} : { nextCursor: value };
|
|
707
1001
|
}
|
|
1002
|
+
function issueSortFieldOf(field) {
|
|
1003
|
+
const upstream = ISSUE_SORT_FIELDS_UPSTREAM[field];
|
|
1004
|
+
if (upstream === void 0) throw new TrackerError("unsupported", `\u0417\u0430\u0434\u0430\u0447\u0438 SourceCraft: \u0441\u043E\u0440\u0442\u0438\u0440\u043E\u0432\u043A\u0430 \u043F\u043E ${field} \u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F.`);
|
|
1005
|
+
return upstream;
|
|
1006
|
+
}
|
|
1007
|
+
function issueFilterOf(query) {
|
|
1008
|
+
const clauses = [];
|
|
1009
|
+
if (query.status !== void 0 && query.status.length > 0) {
|
|
1010
|
+
const slugs = [...new Set(query.status.flatMap((category) => SOURCECRAFT_STATUS_FILTER[category]))];
|
|
1011
|
+
clauses.push(`status in (${slugs.join(", ")})`);
|
|
1012
|
+
}
|
|
1013
|
+
for (const label of query.labels ?? []) clauses.push(`label_slug=${quoted(label)}`);
|
|
1014
|
+
if (typeof query.assignee === "string") clauses.push(`assignee_slug=${quoted(query.assignee)}`);
|
|
1015
|
+
if (query.author !== void 0) clauses.push(`author_slug=${quoted(query.author)}`);
|
|
1016
|
+
if (query.search !== void 0) clauses.push(`title=${quoted(query.search)}`);
|
|
1017
|
+
if (query.createdAfter !== void 0) clauses.push(`created_at>=${quoted(isoTime(query.createdAfter))}`);
|
|
1018
|
+
if (query.createdBefore !== void 0) clauses.push(`created_at<=${quoted(isoTime(query.createdBefore))}`);
|
|
1019
|
+
if (query.updatedAfter !== void 0) clauses.push(`updated_at>=${quoted(isoTime(query.updatedAfter))}`);
|
|
1020
|
+
if (query.updatedBefore !== void 0) clauses.push(`updated_at<=${quoted(isoTime(query.updatedBefore))}`);
|
|
1021
|
+
return clauses.length === 0 ? void 0 : clauses.join(" and ");
|
|
1022
|
+
}
|
|
708
1023
|
function issueOf4(raw, web) {
|
|
709
1024
|
const item = isRecord(raw) ? raw : {};
|
|
710
1025
|
const status = isRecord(item.status) ? item.status : {};
|
|
@@ -794,17 +1109,22 @@ function deleteTrackerToken(dataDir, projectId, sink = defaultSecretSink) {
|
|
|
794
1109
|
}
|
|
795
1110
|
export {
|
|
796
1111
|
ALL_STATUSES,
|
|
1112
|
+
COMMENT_FILTERS,
|
|
797
1113
|
GITHUB_CLOSE_REASONS,
|
|
798
1114
|
GITHUB_IN_PROGRESS_LABEL,
|
|
799
1115
|
GITHUB_PRIORITY_LABELS,
|
|
800
1116
|
GITLAB_IN_PROGRESS_LABEL,
|
|
801
1117
|
GITLAB_PRIORITY_LABELS,
|
|
1118
|
+
ISSUE_FILTERS,
|
|
1119
|
+
ISSUE_SORT_FIELDS,
|
|
802
1120
|
JIRA_ISSUE_TYPE,
|
|
803
1121
|
JIRA_PRIORITIES,
|
|
804
1122
|
JIRA_PRIORITY_NAMES,
|
|
805
1123
|
JIRA_STATUS_CATEGORIES,
|
|
1124
|
+
LEGACY_ISSUE_CAPABILITIES,
|
|
806
1125
|
SOURCECRAFT_PRIORITIES,
|
|
807
1126
|
SOURCECRAFT_STATUSES,
|
|
1127
|
+
SOURCECRAFT_STATUS_FILTER,
|
|
808
1128
|
SOURCECRAFT_STATUS_SLUGS,
|
|
809
1129
|
TRACKER_ERROR_BODY_MAX,
|
|
810
1130
|
TRACKER_LOCAL_AUTHOR,
|
|
@@ -812,20 +1132,28 @@ export {
|
|
|
812
1132
|
TRACKER_SECRETS_FILE,
|
|
813
1133
|
TRACKER_TIMEOUT_MS,
|
|
814
1134
|
TrackerError,
|
|
1135
|
+
assertCommentQuery,
|
|
1136
|
+
assertListQuery,
|
|
815
1137
|
baseUrl,
|
|
816
1138
|
clipText,
|
|
1139
|
+
containsText,
|
|
817
1140
|
deleteTrackerToken,
|
|
818
1141
|
findTracker,
|
|
819
1142
|
githubTracker,
|
|
820
1143
|
gitlabTracker,
|
|
821
1144
|
hasTrackerToken,
|
|
1145
|
+
inTimeRange,
|
|
822
1146
|
isRecord,
|
|
1147
|
+
isoTime,
|
|
1148
|
+
issueCapabilitiesOf,
|
|
823
1149
|
jiraTracker,
|
|
1150
|
+
keysFor,
|
|
824
1151
|
listTrackers,
|
|
825
1152
|
localTracker,
|
|
826
1153
|
lookup,
|
|
827
1154
|
nameOf,
|
|
828
1155
|
priorityName,
|
|
1156
|
+
quoted,
|
|
829
1157
|
readTrackerToken,
|
|
830
1158
|
registerBuiltinTrackers,
|
|
831
1159
|
registerTracker,
|
|
@@ -835,6 +1163,7 @@ export {
|
|
|
835
1163
|
stringList,
|
|
836
1164
|
text,
|
|
837
1165
|
toTimestamp,
|
|
1166
|
+
usedFilters,
|
|
838
1167
|
writeTrackerToken
|
|
839
1168
|
};
|
|
840
1169
|
//# sourceMappingURL=index.js.map
|