@lumy-pack/line-lore 0.0.2 → 0.0.4
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 +280 -42
- package/dist/cache/file-cache.d.ts +3 -0
- package/dist/cache/index.d.ts +2 -0
- package/dist/cache/sharded-cache.d.ts +33 -0
- package/dist/cli.mjs +794 -454
- package/dist/components/TraceResult.d.ts +1 -1
- package/dist/core/blame/blame.d.ts +1 -1
- package/dist/core/core.d.ts +2 -1
- package/dist/core/patch-id/patch-id.d.ts +7 -2
- package/dist/core/pr-lookup/pr-lookup.d.ts +7 -1
- package/dist/index.cjs +767 -429
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +762 -429
- package/dist/platform/github/github-adapter.d.ts +6 -0
- package/dist/platform/github/github-enterprise-adapter.d.ts +2 -0
- package/dist/platform/gitlab/gitlab-adapter.d.ts +6 -0
- package/dist/platform/gitlab/gitlab-self-hosted-adapter.d.ts +2 -0
- package/dist/platform/platform.d.ts +1 -1
- package/dist/platform/scheduler/request-scheduler.d.ts +0 -5
- package/dist/types/graph.d.ts +2 -1
- package/dist/types/pipeline.d.ts +0 -1
- package/dist/types/trace.d.ts +5 -11
- package/dist/version.d.ts +1 -1
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __esm = (fn, res) => function __init() {
|
|
7
9
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
@@ -18,6 +20,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
20
|
}
|
|
19
21
|
return to;
|
|
20
22
|
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
21
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
32
|
|
|
23
33
|
// ../../node_modules/tsup/assets/cjs_shims.js
|
|
@@ -79,6 +89,185 @@ var init_errors = __esm({
|
|
|
79
89
|
}
|
|
80
90
|
});
|
|
81
91
|
|
|
92
|
+
// src/cache/sharded-cache.ts
|
|
93
|
+
function getShardPrefix(key) {
|
|
94
|
+
return key.slice(0, 2).toLowerCase();
|
|
95
|
+
}
|
|
96
|
+
async function cleanupLegacyCache() {
|
|
97
|
+
const legacyDir = (0, import_node_path.join)((0, import_node_os.homedir)(), ".line-lore", "cache");
|
|
98
|
+
try {
|
|
99
|
+
const entries = await (0, import_promises.readdir)(legacyDir, { withFileTypes: true });
|
|
100
|
+
for (const entry of entries) {
|
|
101
|
+
if (entry.isFile() && entry.name.endsWith(".json")) {
|
|
102
|
+
try {
|
|
103
|
+
await (0, import_promises.rm)((0, import_node_path.join)(legacyDir, entry.name), { force: true });
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
var import_promises, import_node_os, import_node_path, DEFAULT_CACHE_BASE, DEFAULT_MAX_ENTRIES_PER_SHARD, ShardedCache;
|
|
112
|
+
var init_sharded_cache = __esm({
|
|
113
|
+
"src/cache/sharded-cache.ts"() {
|
|
114
|
+
"use strict";
|
|
115
|
+
init_cjs_shims();
|
|
116
|
+
import_promises = require("fs/promises");
|
|
117
|
+
import_node_os = require("os");
|
|
118
|
+
import_node_path = require("path");
|
|
119
|
+
DEFAULT_CACHE_BASE = (0, import_node_path.join)((0, import_node_os.homedir)(), ".line-lore", "cache");
|
|
120
|
+
DEFAULT_MAX_ENTRIES_PER_SHARD = 1e3;
|
|
121
|
+
ShardedCache = class {
|
|
122
|
+
baseDir;
|
|
123
|
+
maxEntriesPerShard;
|
|
124
|
+
enabled;
|
|
125
|
+
shards = /* @__PURE__ */ new Map();
|
|
126
|
+
constructor(namespace, options) {
|
|
127
|
+
const cacheBase = options?.cacheBase ?? DEFAULT_CACHE_BASE;
|
|
128
|
+
const repoId = options?.repoId ?? {
|
|
129
|
+
host: "_local",
|
|
130
|
+
owner: "_",
|
|
131
|
+
repo: "_default"
|
|
132
|
+
};
|
|
133
|
+
this.baseDir = (0, import_node_path.join)(
|
|
134
|
+
cacheBase,
|
|
135
|
+
repoId.host,
|
|
136
|
+
repoId.owner,
|
|
137
|
+
repoId.repo,
|
|
138
|
+
namespace
|
|
139
|
+
);
|
|
140
|
+
this.maxEntriesPerShard = options?.maxEntriesPerShard ?? DEFAULT_MAX_ENTRIES_PER_SHARD;
|
|
141
|
+
this.enabled = options?.enabled ?? true;
|
|
142
|
+
}
|
|
143
|
+
async get(key) {
|
|
144
|
+
if (!this.enabled) return null;
|
|
145
|
+
const data = await this.readShard(getShardPrefix(key));
|
|
146
|
+
const entry = data[key];
|
|
147
|
+
return entry?.value ?? null;
|
|
148
|
+
}
|
|
149
|
+
async has(key) {
|
|
150
|
+
if (!this.enabled) return false;
|
|
151
|
+
const data = await this.readShard(getShardPrefix(key));
|
|
152
|
+
return key in data;
|
|
153
|
+
}
|
|
154
|
+
set(key, value) {
|
|
155
|
+
if (!this.enabled) return Promise.resolve();
|
|
156
|
+
const prefix = getShardPrefix(key);
|
|
157
|
+
const state = this.getShardState(prefix);
|
|
158
|
+
state.writeQueue = state.writeQueue.then(() => this.doSet(prefix, key, value)).catch(() => {
|
|
159
|
+
});
|
|
160
|
+
return state.writeQueue;
|
|
161
|
+
}
|
|
162
|
+
delete(key) {
|
|
163
|
+
if (!this.enabled) return Promise.resolve(false);
|
|
164
|
+
const prefix = getShardPrefix(key);
|
|
165
|
+
const state = this.getShardState(prefix);
|
|
166
|
+
let deleted = false;
|
|
167
|
+
state.writeQueue = state.writeQueue.then(async () => {
|
|
168
|
+
const data = await this.readShard(prefix);
|
|
169
|
+
if (key in data) {
|
|
170
|
+
delete data[key];
|
|
171
|
+
state.store = data;
|
|
172
|
+
await this.writeShard(prefix, data);
|
|
173
|
+
deleted = true;
|
|
174
|
+
}
|
|
175
|
+
}).catch(() => {
|
|
176
|
+
});
|
|
177
|
+
return state.writeQueue.then(() => deleted);
|
|
178
|
+
}
|
|
179
|
+
async clear() {
|
|
180
|
+
this.shards.clear();
|
|
181
|
+
try {
|
|
182
|
+
await (0, import_promises.rm)(this.baseDir, { recursive: true, force: true });
|
|
183
|
+
} catch {
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async size() {
|
|
187
|
+
let total = 0;
|
|
188
|
+
try {
|
|
189
|
+
const files = await (0, import_promises.readdir)(this.baseDir);
|
|
190
|
+
for (const file of files) {
|
|
191
|
+
if (!file.endsWith(".json")) continue;
|
|
192
|
+
const prefix = file.replace(".json", "");
|
|
193
|
+
const data = await this.readShard(prefix);
|
|
194
|
+
total += Object.keys(data).length;
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
return total;
|
|
199
|
+
}
|
|
200
|
+
async destroy() {
|
|
201
|
+
this.shards.clear();
|
|
202
|
+
try {
|
|
203
|
+
await (0, import_promises.rm)(this.baseDir, { recursive: true, force: true });
|
|
204
|
+
} catch {
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
getShardState(prefix) {
|
|
208
|
+
let state = this.shards.get(prefix);
|
|
209
|
+
if (!state) {
|
|
210
|
+
state = { store: null, writeQueue: Promise.resolve() };
|
|
211
|
+
this.shards.set(prefix, state);
|
|
212
|
+
}
|
|
213
|
+
return state;
|
|
214
|
+
}
|
|
215
|
+
async doSet(prefix, key, value) {
|
|
216
|
+
const state = this.getShardState(prefix);
|
|
217
|
+
const data = await this.readShard(prefix);
|
|
218
|
+
data[key] = { key, value, createdAt: Date.now() };
|
|
219
|
+
const keys = Object.keys(data);
|
|
220
|
+
if (keys.length > this.maxEntriesPerShard) {
|
|
221
|
+
const sorted = keys.sort((a, b) => data[a].createdAt - data[b].createdAt);
|
|
222
|
+
const toRemove = sorted.slice(0, keys.length - this.maxEntriesPerShard);
|
|
223
|
+
for (const k of toRemove) {
|
|
224
|
+
delete data[k];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
state.store = data;
|
|
228
|
+
await this.writeShard(prefix, data);
|
|
229
|
+
}
|
|
230
|
+
async readShard(prefix) {
|
|
231
|
+
const state = this.getShardState(prefix);
|
|
232
|
+
if (state.store !== null) return state.store;
|
|
233
|
+
const filePath = (0, import_node_path.join)(this.baseDir, `${prefix}.json`);
|
|
234
|
+
try {
|
|
235
|
+
const content = await (0, import_promises.readFile)(filePath, "utf-8");
|
|
236
|
+
state.store = JSON.parse(content);
|
|
237
|
+
return state.store;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
if (error instanceof SyntaxError || error instanceof Error && "code" in error && error.code === "ERR_INVALID_JSON") {
|
|
240
|
+
console.warn(
|
|
241
|
+
`[line-lore] Cache shard corrupted, resetting: ${filePath}`
|
|
242
|
+
);
|
|
243
|
+
state.store = {};
|
|
244
|
+
await this.writeShard(prefix, {});
|
|
245
|
+
return state.store;
|
|
246
|
+
}
|
|
247
|
+
state.store = {};
|
|
248
|
+
return state.store;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
async writeShard(prefix, data) {
|
|
252
|
+
await (0, import_promises.mkdir)(this.baseDir, { recursive: true });
|
|
253
|
+
const filePath = (0, import_node_path.join)(this.baseDir, `${prefix}.json`);
|
|
254
|
+
const tmpPath = `${filePath}.tmp`;
|
|
255
|
+
await (0, import_promises.writeFile)(tmpPath, JSON.stringify(data), "utf-8");
|
|
256
|
+
await (0, import_promises.rename)(tmpPath, filePath);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// src/cache/index.ts
|
|
263
|
+
var init_cache = __esm({
|
|
264
|
+
"src/cache/index.ts"() {
|
|
265
|
+
"use strict";
|
|
266
|
+
init_cjs_shims();
|
|
267
|
+
init_sharded_cache();
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
82
271
|
// src/git/executor.ts
|
|
83
272
|
async function execCommand(command, args, options, errorCode) {
|
|
84
273
|
const { cwd, timeout, allowExitCodes = [] } = options ?? {};
|
|
@@ -91,9 +280,10 @@ async function execCommand(command, args, options, errorCode) {
|
|
|
91
280
|
});
|
|
92
281
|
const exitCode = result.exitCode ?? 0;
|
|
93
282
|
if (exitCode !== 0 && !allowExitCodes.includes(exitCode)) {
|
|
283
|
+
const isNotGitRepo = exitCode === 128 && command === "git" && /not a git repository/i.test(result.stderr);
|
|
94
284
|
throw new LineLoreError(
|
|
95
|
-
failCode,
|
|
96
|
-
`${command} ${args[0]} failed with exit code ${exitCode}: ${result.stderr}`,
|
|
285
|
+
isNotGitRepo ? LineLoreErrorCode.NOT_GIT_REPO : failCode,
|
|
286
|
+
isNotGitRepo ? `Not a git repository: ${result.stderr.trim()}` : `${command} ${args[0]} failed with exit code ${exitCode}: ${result.stderr}`,
|
|
97
287
|
{ command, args, exitCode, stderr: result.stderr, cwd }
|
|
98
288
|
);
|
|
99
289
|
}
|
|
@@ -145,116 +335,26 @@ var init_executor = __esm({
|
|
|
145
335
|
}
|
|
146
336
|
});
|
|
147
337
|
|
|
148
|
-
// src/cache/file-cache.ts
|
|
149
|
-
var import_promises, import_node_os, import_node_path, DEFAULT_CACHE_DIR, DEFAULT_MAX_ENTRIES, FileCache;
|
|
150
|
-
var init_file_cache = __esm({
|
|
151
|
-
"src/cache/file-cache.ts"() {
|
|
152
|
-
"use strict";
|
|
153
|
-
init_cjs_shims();
|
|
154
|
-
import_promises = require("fs/promises");
|
|
155
|
-
import_node_os = require("os");
|
|
156
|
-
import_node_path = require("path");
|
|
157
|
-
DEFAULT_CACHE_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".line-lore", "cache");
|
|
158
|
-
DEFAULT_MAX_ENTRIES = 1e4;
|
|
159
|
-
FileCache = class {
|
|
160
|
-
filePath;
|
|
161
|
-
maxEntries;
|
|
162
|
-
writeQueue = Promise.resolve();
|
|
163
|
-
constructor(fileName, options) {
|
|
164
|
-
const cacheDir = options?.cacheDir ?? DEFAULT_CACHE_DIR;
|
|
165
|
-
this.filePath = (0, import_node_path.join)(cacheDir, fileName);
|
|
166
|
-
this.maxEntries = options?.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
167
|
-
}
|
|
168
|
-
async get(key) {
|
|
169
|
-
const data = await this.readStore();
|
|
170
|
-
const entry = data[key];
|
|
171
|
-
return entry?.value ?? null;
|
|
172
|
-
}
|
|
173
|
-
async has(key) {
|
|
174
|
-
const data = await this.readStore();
|
|
175
|
-
return key in data;
|
|
176
|
-
}
|
|
177
|
-
set(key, value) {
|
|
178
|
-
this.writeQueue = this.writeQueue.then(() => this.doSet(key, value)).catch(() => {
|
|
179
|
-
});
|
|
180
|
-
return this.writeQueue;
|
|
181
|
-
}
|
|
182
|
-
delete(key) {
|
|
183
|
-
let deleted = false;
|
|
184
|
-
this.writeQueue = this.writeQueue.then(async () => {
|
|
185
|
-
const data = await this.readStore();
|
|
186
|
-
if (key in data) {
|
|
187
|
-
delete data[key];
|
|
188
|
-
await this.writeStore(data);
|
|
189
|
-
deleted = true;
|
|
190
|
-
}
|
|
191
|
-
}).catch(() => {
|
|
192
|
-
});
|
|
193
|
-
return this.writeQueue.then(() => deleted);
|
|
194
|
-
}
|
|
195
|
-
clear() {
|
|
196
|
-
this.writeQueue = this.writeQueue.then(() => this.writeStore({})).catch(() => {
|
|
197
|
-
});
|
|
198
|
-
return this.writeQueue;
|
|
199
|
-
}
|
|
200
|
-
async size() {
|
|
201
|
-
const data = await this.readStore();
|
|
202
|
-
return Object.keys(data).length;
|
|
203
|
-
}
|
|
204
|
-
async doSet(key, value) {
|
|
205
|
-
const data = await this.readStore();
|
|
206
|
-
data[key] = { key, value, createdAt: Date.now() };
|
|
207
|
-
const keys = Object.keys(data);
|
|
208
|
-
if (keys.length > this.maxEntries) {
|
|
209
|
-
const sorted = keys.sort((a, b) => data[a].createdAt - data[b].createdAt);
|
|
210
|
-
const toRemove = sorted.slice(0, keys.length - this.maxEntries);
|
|
211
|
-
for (const k of toRemove) {
|
|
212
|
-
delete data[k];
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
await this.writeStore(data);
|
|
216
|
-
}
|
|
217
|
-
async readStore() {
|
|
218
|
-
try {
|
|
219
|
-
const content = await (0, import_promises.readFile)(this.filePath, "utf-8");
|
|
220
|
-
return JSON.parse(content);
|
|
221
|
-
} catch (error) {
|
|
222
|
-
if (error instanceof SyntaxError || error instanceof Error && "code" in error && error.code === "ERR_INVALID_JSON") {
|
|
223
|
-
console.warn(
|
|
224
|
-
`[line-lore] Cache file corrupted, resetting: ${this.filePath}`
|
|
225
|
-
);
|
|
226
|
-
await this.writeStore({});
|
|
227
|
-
return {};
|
|
228
|
-
}
|
|
229
|
-
return {};
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
async writeStore(data) {
|
|
233
|
-
const dir = (0, import_node_path.join)(this.filePath, "..");
|
|
234
|
-
await (0, import_promises.mkdir)(dir, { recursive: true });
|
|
235
|
-
const tmpPath = `${this.filePath}.tmp`;
|
|
236
|
-
await (0, import_promises.writeFile)(tmpPath, JSON.stringify(data), "utf-8");
|
|
237
|
-
await (0, import_promises.rename)(tmpPath, this.filePath);
|
|
238
|
-
}
|
|
239
|
-
async destroy() {
|
|
240
|
-
try {
|
|
241
|
-
await (0, import_promises.unlink)(this.filePath);
|
|
242
|
-
} catch {
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
|
-
|
|
249
338
|
// src/core/ancestry/ancestry.ts
|
|
250
339
|
async function findMergeCommit(commitSha, options) {
|
|
251
340
|
const ref = options?.ref ?? "HEAD";
|
|
341
|
+
const firstParentResult = await findMergeCommitWithArgs(
|
|
342
|
+
commitSha,
|
|
343
|
+
ref,
|
|
344
|
+
["--first-parent"],
|
|
345
|
+
options
|
|
346
|
+
);
|
|
347
|
+
if (firstParentResult) return firstParentResult;
|
|
348
|
+
return findMergeCommitWithArgs(commitSha, ref, [], options);
|
|
349
|
+
}
|
|
350
|
+
async function findMergeCommitWithArgs(commitSha, ref, extraArgs, options) {
|
|
252
351
|
try {
|
|
253
352
|
const result = await gitExec(
|
|
254
353
|
[
|
|
255
354
|
"log",
|
|
256
355
|
"--merges",
|
|
257
356
|
"--ancestry-path",
|
|
357
|
+
...extraArgs,
|
|
258
358
|
`${commitSha}..${ref}`,
|
|
259
359
|
"--topo-order",
|
|
260
360
|
"--reverse",
|
|
@@ -262,28 +362,30 @@ async function findMergeCommit(commitSha, options) {
|
|
|
262
362
|
],
|
|
263
363
|
{ cwd: options?.cwd, timeout: options?.timeout }
|
|
264
364
|
);
|
|
265
|
-
const lines = result.stdout.trim().split("\n").
|
|
365
|
+
const lines = (0, import_common_utils9.filter)(result.stdout.trim().split("\n"), import_common_utils9.isTruthy);
|
|
266
366
|
if (lines.length === 0) return null;
|
|
267
|
-
|
|
268
|
-
const parts = firstLine.split(" ");
|
|
269
|
-
if (parts.length < 3) return null;
|
|
270
|
-
const mergeCommitSha = parts[0];
|
|
271
|
-
const parentShas = [];
|
|
272
|
-
let subjectStart = 1;
|
|
273
|
-
for (let i = 1; i < parts.length; i++) {
|
|
274
|
-
if (/^[0-9a-f]{40}$/.test(parts[i])) {
|
|
275
|
-
parentShas.push(parts[i]);
|
|
276
|
-
subjectStart = i + 1;
|
|
277
|
-
} else {
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
const subject = parts.slice(subjectStart).join(" ");
|
|
282
|
-
return { mergeCommitSha, parentShas, subject };
|
|
367
|
+
return parseMergeLogLine(lines[0]);
|
|
283
368
|
} catch {
|
|
284
369
|
return null;
|
|
285
370
|
}
|
|
286
371
|
}
|
|
372
|
+
function parseMergeLogLine(line) {
|
|
373
|
+
const parts = line.split(" ");
|
|
374
|
+
if (parts.length < 3) return null;
|
|
375
|
+
const mergeCommitSha = parts[0];
|
|
376
|
+
const parentShas = [];
|
|
377
|
+
let subjectStart = 1;
|
|
378
|
+
for (let i = 1; i < parts.length; i++) {
|
|
379
|
+
if (/^[0-9a-f]{40}$/.test(parts[i])) {
|
|
380
|
+
parentShas.push(parts[i]);
|
|
381
|
+
subjectStart = i + 1;
|
|
382
|
+
} else {
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
const subject = parts.slice(subjectStart).join(" ");
|
|
387
|
+
return { mergeCommitSha, parentShas, subject };
|
|
388
|
+
}
|
|
287
389
|
function extractPRFromMergeMessage(subject) {
|
|
288
390
|
const ghMatch = /Merge pull request #(\d+)/.exec(subject);
|
|
289
391
|
if (ghMatch) return parseInt(ghMatch[1], 10);
|
|
@@ -293,10 +395,12 @@ function extractPRFromMergeMessage(subject) {
|
|
|
293
395
|
if (glMatch) return parseInt(glMatch[1], 10);
|
|
294
396
|
return null;
|
|
295
397
|
}
|
|
398
|
+
var import_common_utils9;
|
|
296
399
|
var init_ancestry = __esm({
|
|
297
400
|
"src/core/ancestry/ancestry.ts"() {
|
|
298
401
|
"use strict";
|
|
299
402
|
init_cjs_shims();
|
|
403
|
+
import_common_utils9 = require("@winglet/common-utils");
|
|
300
404
|
init_executor();
|
|
301
405
|
}
|
|
302
406
|
});
|
|
@@ -311,14 +415,25 @@ var init_ancestry2 = __esm({
|
|
|
311
415
|
});
|
|
312
416
|
|
|
313
417
|
// src/core/patch-id/patch-id.ts
|
|
314
|
-
function
|
|
315
|
-
|
|
316
|
-
|
|
418
|
+
function repoKey(repoId) {
|
|
419
|
+
return `${repoId.host}/${repoId.owner}/${repoId.repo}`;
|
|
420
|
+
}
|
|
421
|
+
function getCache(repoId, noCache) {
|
|
422
|
+
if (noCache) {
|
|
423
|
+
return new ShardedCache("patch-id", { repoId, enabled: false });
|
|
317
424
|
}
|
|
318
|
-
|
|
425
|
+
const key = repoKey(
|
|
426
|
+
repoId ?? { host: "_local", owner: "_", repo: "_default" }
|
|
427
|
+
);
|
|
428
|
+
let cache = cacheRegistry.get(key);
|
|
429
|
+
if (!cache) {
|
|
430
|
+
cache = new ShardedCache("patch-id", { repoId });
|
|
431
|
+
cacheRegistry.set(key, cache);
|
|
432
|
+
}
|
|
433
|
+
return cache;
|
|
319
434
|
}
|
|
320
435
|
async function computePatchId(commitSha, options) {
|
|
321
|
-
const cache = getCache();
|
|
436
|
+
const cache = getCache(options?.repoId, options?.noCache);
|
|
322
437
|
const cached = await cache.get(commitSha);
|
|
323
438
|
if (cached) return cached;
|
|
324
439
|
try {
|
|
@@ -349,7 +464,7 @@ async function findPatchIdMatch(commitSha, options) {
|
|
|
349
464
|
["log", "--format=%H", `-${scanDepth}`, ref],
|
|
350
465
|
{ cwd: options?.cwd, timeout: options?.timeout }
|
|
351
466
|
);
|
|
352
|
-
const candidates = logResult.stdout.trim().split("\n").
|
|
467
|
+
const candidates = (0, import_common_utils10.filter)(logResult.stdout.trim().split("\n"), import_common_utils10.isTruthy);
|
|
353
468
|
for (const candidateSha of candidates) {
|
|
354
469
|
if (candidateSha === commitSha) continue;
|
|
355
470
|
const candidatePatchId = await computePatchId(candidateSha, options);
|
|
@@ -362,17 +477,18 @@ async function findPatchIdMatch(commitSha, options) {
|
|
|
362
477
|
return null;
|
|
363
478
|
}
|
|
364
479
|
function resetPatchIdCache() {
|
|
365
|
-
|
|
480
|
+
cacheRegistry.clear();
|
|
366
481
|
}
|
|
367
|
-
var DEFAULT_SCAN_DEPTH,
|
|
482
|
+
var import_common_utils10, DEFAULT_SCAN_DEPTH, cacheRegistry;
|
|
368
483
|
var init_patch_id = __esm({
|
|
369
484
|
"src/core/patch-id/patch-id.ts"() {
|
|
370
485
|
"use strict";
|
|
371
486
|
init_cjs_shims();
|
|
372
|
-
|
|
487
|
+
import_common_utils10 = require("@winglet/common-utils");
|
|
488
|
+
init_cache();
|
|
373
489
|
init_executor();
|
|
374
490
|
DEFAULT_SCAN_DEPTH = 500;
|
|
375
|
-
|
|
491
|
+
cacheRegistry = /* @__PURE__ */ new Map();
|
|
376
492
|
}
|
|
377
493
|
});
|
|
378
494
|
|
|
@@ -392,40 +508,58 @@ var init_patch_id2 = __esm({
|
|
|
392
508
|
});
|
|
393
509
|
|
|
394
510
|
// src/core/pr-lookup/pr-lookup.ts
|
|
395
|
-
function
|
|
396
|
-
|
|
397
|
-
|
|
511
|
+
function repoKey2(repoId) {
|
|
512
|
+
return `${repoId.host}/${repoId.owner}/${repoId.repo}`;
|
|
513
|
+
}
|
|
514
|
+
function getCache2(repoId, noCache) {
|
|
515
|
+
if (noCache) {
|
|
516
|
+
return new ShardedCache("pr", { repoId, enabled: false });
|
|
398
517
|
}
|
|
399
|
-
|
|
518
|
+
const key = repoKey2(
|
|
519
|
+
repoId ?? { host: "_local", owner: "_", repo: "_default" }
|
|
520
|
+
);
|
|
521
|
+
let cache = cacheRegistry2.get(key);
|
|
522
|
+
if (!cache) {
|
|
523
|
+
cache = new ShardedCache("pr", { repoId });
|
|
524
|
+
cacheRegistry2.set(key, cache);
|
|
525
|
+
}
|
|
526
|
+
return cache;
|
|
400
527
|
}
|
|
401
528
|
async function lookupPR(commitSha, adapter, options) {
|
|
402
|
-
const cache = getCache2();
|
|
529
|
+
const cache = getCache2(options?.repoId, options?.noCache);
|
|
403
530
|
const cached = await cache.get(commitSha);
|
|
404
531
|
if (cached) return cached;
|
|
532
|
+
let mergeBasedPR = null;
|
|
405
533
|
const mergeResult = await findMergeCommit(commitSha, options);
|
|
406
534
|
if (mergeResult) {
|
|
407
535
|
const prNumber = extractPRFromMergeMessage(mergeResult.subject);
|
|
408
536
|
if (prNumber) {
|
|
409
537
|
if (adapter) {
|
|
410
538
|
const prInfo = await adapter.getPRForCommit(mergeResult.mergeCommitSha);
|
|
411
|
-
if (prInfo) {
|
|
412
|
-
|
|
413
|
-
return prInfo;
|
|
539
|
+
if (prInfo?.mergedAt) {
|
|
540
|
+
mergeBasedPR = prInfo;
|
|
414
541
|
}
|
|
415
542
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
543
|
+
if (!mergeBasedPR) {
|
|
544
|
+
mergeBasedPR = {
|
|
545
|
+
number: prNumber,
|
|
546
|
+
title: mergeResult.subject,
|
|
547
|
+
author: "",
|
|
548
|
+
url: "",
|
|
549
|
+
mergeCommit: mergeResult.mergeCommitSha,
|
|
550
|
+
baseBranch: ""
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
if (!options?.deep || mergeBasedPR.mergedAt) {
|
|
554
|
+
await cache.set(commitSha, mergeBasedPR);
|
|
555
|
+
return mergeBasedPR;
|
|
556
|
+
}
|
|
426
557
|
}
|
|
427
558
|
}
|
|
428
|
-
const patchIdMatch = await findPatchIdMatch(commitSha,
|
|
559
|
+
const patchIdMatch = await findPatchIdMatch(commitSha, {
|
|
560
|
+
...options,
|
|
561
|
+
scanDepth: options?.deep ? DEEP_SCAN_DEPTH : void 0
|
|
562
|
+
});
|
|
429
563
|
if (patchIdMatch) {
|
|
430
564
|
const result = await lookupPR(patchIdMatch.matchedSha, adapter, options);
|
|
431
565
|
if (result) {
|
|
@@ -433,9 +567,13 @@ async function lookupPR(commitSha, adapter, options) {
|
|
|
433
567
|
return result;
|
|
434
568
|
}
|
|
435
569
|
}
|
|
570
|
+
if (mergeBasedPR) {
|
|
571
|
+
await cache.set(commitSha, mergeBasedPR);
|
|
572
|
+
return mergeBasedPR;
|
|
573
|
+
}
|
|
436
574
|
if (adapter) {
|
|
437
575
|
const prInfo = await adapter.getPRForCommit(commitSha);
|
|
438
|
-
if (prInfo) {
|
|
576
|
+
if (prInfo?.mergedAt) {
|
|
439
577
|
await cache.set(commitSha, prInfo);
|
|
440
578
|
return prInfo;
|
|
441
579
|
}
|
|
@@ -443,17 +581,18 @@ async function lookupPR(commitSha, adapter, options) {
|
|
|
443
581
|
return null;
|
|
444
582
|
}
|
|
445
583
|
function resetPRCache() {
|
|
446
|
-
|
|
584
|
+
cacheRegistry2.clear();
|
|
447
585
|
}
|
|
448
|
-
var
|
|
586
|
+
var cacheRegistry2, DEEP_SCAN_DEPTH;
|
|
449
587
|
var init_pr_lookup = __esm({
|
|
450
588
|
"src/core/pr-lookup/pr-lookup.ts"() {
|
|
451
589
|
"use strict";
|
|
452
590
|
init_cjs_shims();
|
|
453
|
-
|
|
591
|
+
init_cache();
|
|
454
592
|
init_ancestry2();
|
|
455
593
|
init_patch_id2();
|
|
456
|
-
|
|
594
|
+
cacheRegistry2 = /* @__PURE__ */ new Map();
|
|
595
|
+
DEEP_SCAN_DEPTH = 2e3;
|
|
457
596
|
}
|
|
458
597
|
});
|
|
459
598
|
|
|
@@ -477,6 +616,7 @@ __export(src_exports, {
|
|
|
477
616
|
LineLoreError: () => LineLoreError,
|
|
478
617
|
LineLoreErrorCode: () => LineLoreErrorCode,
|
|
479
618
|
clearCache: () => clearCache,
|
|
619
|
+
graph: () => graph,
|
|
480
620
|
health: () => health,
|
|
481
621
|
trace: () => trace,
|
|
482
622
|
traverseIssueGraph: () => traverseIssueGraph
|
|
@@ -487,12 +627,15 @@ init_errors();
|
|
|
487
627
|
|
|
488
628
|
// src/core/core.ts
|
|
489
629
|
init_cjs_shims();
|
|
630
|
+
var import_node_crypto2 = require("crypto");
|
|
631
|
+
var import_common_utils11 = require("@winglet/common-utils");
|
|
490
632
|
|
|
491
633
|
// src/ast/index.ts
|
|
492
634
|
init_cjs_shims();
|
|
493
635
|
|
|
494
636
|
// src/ast/parser.ts
|
|
495
637
|
init_cjs_shims();
|
|
638
|
+
var import_common_utils = require("@winglet/common-utils");
|
|
496
639
|
var astGrep = null;
|
|
497
640
|
var loadAttempted = false;
|
|
498
641
|
var available = false;
|
|
@@ -543,7 +686,7 @@ async function findSymbols(source, lang) {
|
|
|
543
686
|
try {
|
|
544
687
|
const { parse, Lang } = astGrep;
|
|
545
688
|
const langEnum = Lang[lang] ?? Lang[lang.charAt(0).toUpperCase() + lang.slice(1)];
|
|
546
|
-
if (
|
|
689
|
+
if ((0, import_common_utils.isNil)(langEnum)) return [];
|
|
547
690
|
const root = parse(langEnum, source).root();
|
|
548
691
|
const symbols = [];
|
|
549
692
|
const kindPatterns = [
|
|
@@ -695,8 +838,14 @@ function findPythonBlockEnd(lines, startIdx) {
|
|
|
695
838
|
return lines.length - 1;
|
|
696
839
|
}
|
|
697
840
|
|
|
841
|
+
// src/core/core.ts
|
|
842
|
+
init_cache();
|
|
843
|
+
init_errors();
|
|
844
|
+
init_executor();
|
|
845
|
+
|
|
698
846
|
// src/git/health.ts
|
|
699
847
|
init_cjs_shims();
|
|
848
|
+
var import_common_utils2 = require("@winglet/common-utils");
|
|
700
849
|
init_executor();
|
|
701
850
|
var GIT_VERSION_PATTERN = /git version (\d+\.\d+\.\d+)/;
|
|
702
851
|
var BLOOM_FILTER_MIN_VERSION = [2, 27, 0];
|
|
@@ -705,7 +854,7 @@ function parseGitVersion(versionStr) {
|
|
|
705
854
|
return match?.[1] ?? "0.0.0";
|
|
706
855
|
}
|
|
707
856
|
function isVersionAtLeast(version, minVersion) {
|
|
708
|
-
const parts = version.split(".")
|
|
857
|
+
const parts = (0, import_common_utils2.map)(version.split("."), Number);
|
|
709
858
|
for (let i = 0; i < 3; i++) {
|
|
710
859
|
if ((parts[i] ?? 0) > minVersion[i]) return true;
|
|
711
860
|
if ((parts[i] ?? 0) < minVersion[i]) return false;
|
|
@@ -787,6 +936,7 @@ init_cjs_shims();
|
|
|
787
936
|
|
|
788
937
|
// src/platform/github/github-adapter.ts
|
|
789
938
|
init_cjs_shims();
|
|
939
|
+
var import_common_utils3 = require("@winglet/common-utils");
|
|
790
940
|
init_executor();
|
|
791
941
|
|
|
792
942
|
// src/platform/scheduler/index.ts
|
|
@@ -797,8 +947,6 @@ init_cjs_shims();
|
|
|
797
947
|
var RequestScheduler = class {
|
|
798
948
|
rateLimitInfo = null;
|
|
799
949
|
threshold;
|
|
800
|
-
etagCache = /* @__PURE__ */ new Map();
|
|
801
|
-
responseCache = /* @__PURE__ */ new Map();
|
|
802
950
|
constructor(options) {
|
|
803
951
|
this.threshold = options?.rateLimitThreshold ?? 10;
|
|
804
952
|
}
|
|
@@ -812,16 +960,6 @@ var RequestScheduler = class {
|
|
|
812
960
|
getRateLimit() {
|
|
813
961
|
return this.rateLimitInfo;
|
|
814
962
|
}
|
|
815
|
-
setEtag(url, etag, response) {
|
|
816
|
-
this.etagCache.set(url, etag);
|
|
817
|
-
this.responseCache.set(url, response);
|
|
818
|
-
}
|
|
819
|
-
getEtag(url) {
|
|
820
|
-
return this.etagCache.get(url) ?? null;
|
|
821
|
-
}
|
|
822
|
-
getCachedResponse(url) {
|
|
823
|
-
return this.responseCache.get(url) ?? null;
|
|
824
|
-
}
|
|
825
963
|
};
|
|
826
964
|
|
|
827
965
|
// src/platform/github/github-adapter.ts
|
|
@@ -829,9 +967,14 @@ var GitHubAdapter = class {
|
|
|
829
967
|
platform = "github";
|
|
830
968
|
scheduler;
|
|
831
969
|
hostname;
|
|
970
|
+
defaultBranchCache = null;
|
|
971
|
+
remoteName;
|
|
972
|
+
cwd;
|
|
832
973
|
constructor(options) {
|
|
833
974
|
this.hostname = options?.hostname ?? "github.com";
|
|
834
975
|
this.scheduler = options?.scheduler ?? new RequestScheduler();
|
|
976
|
+
this.remoteName = options?.remoteName ?? "origin";
|
|
977
|
+
this.cwd = options?.cwd;
|
|
835
978
|
}
|
|
836
979
|
async checkAuth() {
|
|
837
980
|
try {
|
|
@@ -839,6 +982,7 @@ var GitHubAdapter = class {
|
|
|
839
982
|
"gh",
|
|
840
983
|
["auth", "token", "--hostname", this.hostname],
|
|
841
984
|
{
|
|
985
|
+
cwd: this.cwd,
|
|
842
986
|
allowExitCodes: [1]
|
|
843
987
|
}
|
|
844
988
|
);
|
|
@@ -854,64 +998,98 @@ var GitHubAdapter = class {
|
|
|
854
998
|
async getPRForCommit(sha) {
|
|
855
999
|
if (this.scheduler.isRateLimited()) return null;
|
|
856
1000
|
try {
|
|
857
|
-
const result = await shellExec(
|
|
858
|
-
"
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1001
|
+
const result = await shellExec(
|
|
1002
|
+
"gh",
|
|
1003
|
+
[
|
|
1004
|
+
"api",
|
|
1005
|
+
`repos/{owner}/{repo}/commits/${sha}/pulls`,
|
|
1006
|
+
"--hostname",
|
|
1007
|
+
this.hostname,
|
|
1008
|
+
"--jq",
|
|
1009
|
+
"[.[] | select(.merged_at != null) | {number, title, user: .user.login, html_url, merge_commit_sha, base: .base.ref, merged_at}] | sort_by(.merged_at)"
|
|
1010
|
+
],
|
|
1011
|
+
{ cwd: this.cwd }
|
|
1012
|
+
);
|
|
1013
|
+
const prs = JSON.parse(result.stdout);
|
|
1014
|
+
if (!(0, import_common_utils3.isArray)(prs) || prs.length === 0) return null;
|
|
1015
|
+
const defaultBranch = await this.detectDefaultBranch();
|
|
1016
|
+
const defaultBranchPR = prs.find(
|
|
1017
|
+
(pr) => pr.base === defaultBranch
|
|
1018
|
+
);
|
|
1019
|
+
const data = defaultBranchPR ?? prs[0];
|
|
867
1020
|
return {
|
|
868
1021
|
number: data.number,
|
|
869
1022
|
title: data.title ?? "",
|
|
870
1023
|
author: data.user ?? "",
|
|
871
1024
|
url: data.html_url ?? "",
|
|
872
1025
|
mergeCommit: data.merge_commit_sha ?? sha,
|
|
873
|
-
baseBranch: data.base ??
|
|
1026
|
+
baseBranch: data.base ?? defaultBranch,
|
|
874
1027
|
mergedAt: data.merged_at
|
|
875
1028
|
};
|
|
876
1029
|
} catch {
|
|
877
1030
|
return null;
|
|
878
1031
|
}
|
|
879
1032
|
}
|
|
1033
|
+
async detectDefaultBranch() {
|
|
1034
|
+
if (this.defaultBranchCache) return this.defaultBranchCache;
|
|
1035
|
+
try {
|
|
1036
|
+
const result = await gitExec(
|
|
1037
|
+
["symbolic-ref", `refs/remotes/${this.remoteName}/HEAD`],
|
|
1038
|
+
{ cwd: this.cwd }
|
|
1039
|
+
);
|
|
1040
|
+
const ref = result.stdout.trim();
|
|
1041
|
+
this.defaultBranchCache = ref.replace(`refs/remotes/${this.remoteName}/`, "") || "main";
|
|
1042
|
+
return this.defaultBranchCache;
|
|
1043
|
+
} catch {
|
|
1044
|
+
return "main";
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
880
1047
|
async getPRCommits(prNumber) {
|
|
881
1048
|
try {
|
|
882
|
-
const result = await shellExec(
|
|
883
|
-
"
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
1049
|
+
const result = await shellExec(
|
|
1050
|
+
"gh",
|
|
1051
|
+
[
|
|
1052
|
+
"api",
|
|
1053
|
+
`repos/{owner}/{repo}/pulls/${prNumber}/commits`,
|
|
1054
|
+
"--hostname",
|
|
1055
|
+
this.hostname,
|
|
1056
|
+
"--jq",
|
|
1057
|
+
".[].sha"
|
|
1058
|
+
],
|
|
1059
|
+
{ cwd: this.cwd }
|
|
1060
|
+
);
|
|
1061
|
+
return (0, import_common_utils3.filter)(result.stdout.trim().split("\n"), import_common_utils3.isTruthy);
|
|
891
1062
|
} catch {
|
|
892
1063
|
return [];
|
|
893
1064
|
}
|
|
894
1065
|
}
|
|
895
1066
|
async getLinkedIssues(prNumber) {
|
|
896
1067
|
try {
|
|
897
|
-
const result = await shellExec(
|
|
898
|
-
"
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1068
|
+
const result = await shellExec(
|
|
1069
|
+
"gh",
|
|
1070
|
+
[
|
|
1071
|
+
"api",
|
|
1072
|
+
"graphql",
|
|
1073
|
+
"--hostname",
|
|
1074
|
+
this.hostname,
|
|
1075
|
+
"-f",
|
|
1076
|
+
`query=query { repository(owner: "{owner}", name: "{repo}") { pullRequest(number: ${prNumber}) { closingIssuesReferences(first: 10) { nodes { number title url state labels(first: 5) { nodes { name } } } } } } }`,
|
|
1077
|
+
"--jq",
|
|
1078
|
+
".data.repository.pullRequest.closingIssuesReferences.nodes"
|
|
1079
|
+
],
|
|
1080
|
+
{ cwd: this.cwd }
|
|
1081
|
+
);
|
|
907
1082
|
const nodes = JSON.parse(result.stdout);
|
|
908
|
-
if (!
|
|
909
|
-
return
|
|
1083
|
+
if (!(0, import_common_utils3.isArray)(nodes)) return [];
|
|
1084
|
+
return (0, import_common_utils3.map)(nodes, (node) => ({
|
|
910
1085
|
number: node.number,
|
|
911
1086
|
title: node.title ?? "",
|
|
912
1087
|
url: node.url ?? "",
|
|
913
1088
|
state: (node.state ?? "open").toLowerCase(),
|
|
914
|
-
labels: (
|
|
1089
|
+
labels: (0, import_common_utils3.map)(
|
|
1090
|
+
node.labels?.nodes ?? [],
|
|
1091
|
+
(l) => l.name
|
|
1092
|
+
)
|
|
915
1093
|
}));
|
|
916
1094
|
} catch {
|
|
917
1095
|
return [];
|
|
@@ -919,23 +1097,28 @@ var GitHubAdapter = class {
|
|
|
919
1097
|
}
|
|
920
1098
|
async getLinkedPRs(issueNumber) {
|
|
921
1099
|
try {
|
|
922
|
-
const result = await shellExec(
|
|
923
|
-
"
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1100
|
+
const result = await shellExec(
|
|
1101
|
+
"gh",
|
|
1102
|
+
[
|
|
1103
|
+
"api",
|
|
1104
|
+
`repos/{owner}/{repo}/issues/${issueNumber}/timeline`,
|
|
1105
|
+
"--hostname",
|
|
1106
|
+
this.hostname,
|
|
1107
|
+
"--jq",
|
|
1108
|
+
"[.[] | select(.source.issue.pull_request) | .source.issue] | map({number, title, user: .user.login, html_url, merge_commit_sha: .pull_request.merge_commit_sha, merged_at: .pull_request.merged_at})"
|
|
1109
|
+
],
|
|
1110
|
+
{ cwd: this.cwd }
|
|
1111
|
+
);
|
|
930
1112
|
const prs = JSON.parse(result.stdout);
|
|
931
|
-
if (!
|
|
932
|
-
|
|
1113
|
+
if (!(0, import_common_utils3.isArray)(prs)) return [];
|
|
1114
|
+
const defaultBranch = await this.detectDefaultBranch();
|
|
1115
|
+
return (0, import_common_utils3.map)(prs, (pr) => ({
|
|
933
1116
|
number: pr.number,
|
|
934
1117
|
title: pr.title ?? "",
|
|
935
1118
|
author: pr.user ?? "",
|
|
936
1119
|
url: pr.html_url ?? "",
|
|
937
1120
|
mergeCommit: pr.merge_commit_sha ?? "",
|
|
938
|
-
baseBranch:
|
|
1121
|
+
baseBranch: defaultBranch,
|
|
939
1122
|
mergedAt: pr.merged_at
|
|
940
1123
|
}));
|
|
941
1124
|
} catch {
|
|
@@ -944,14 +1127,18 @@ var GitHubAdapter = class {
|
|
|
944
1127
|
}
|
|
945
1128
|
async getRateLimit() {
|
|
946
1129
|
try {
|
|
947
|
-
const result = await shellExec(
|
|
948
|
-
"
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1130
|
+
const result = await shellExec(
|
|
1131
|
+
"gh",
|
|
1132
|
+
[
|
|
1133
|
+
"api",
|
|
1134
|
+
"rate_limit",
|
|
1135
|
+
"--hostname",
|
|
1136
|
+
this.hostname,
|
|
1137
|
+
"--jq",
|
|
1138
|
+
".rate | {limit, remaining, reset}"
|
|
1139
|
+
],
|
|
1140
|
+
{ cwd: this.cwd }
|
|
1141
|
+
);
|
|
955
1142
|
const data = JSON.parse(result.stdout);
|
|
956
1143
|
const info = {
|
|
957
1144
|
limit: data.limit ?? 5e3,
|
|
@@ -971,7 +1158,12 @@ init_cjs_shims();
|
|
|
971
1158
|
var GitHubEnterpriseAdapter = class extends GitHubAdapter {
|
|
972
1159
|
platform = "github-enterprise";
|
|
973
1160
|
constructor(hostname, options) {
|
|
974
|
-
super({
|
|
1161
|
+
super({
|
|
1162
|
+
hostname,
|
|
1163
|
+
scheduler: options?.scheduler,
|
|
1164
|
+
remoteName: options?.remoteName,
|
|
1165
|
+
cwd: options?.cwd
|
|
1166
|
+
});
|
|
975
1167
|
}
|
|
976
1168
|
};
|
|
977
1169
|
|
|
@@ -980,14 +1172,20 @@ init_cjs_shims();
|
|
|
980
1172
|
|
|
981
1173
|
// src/platform/gitlab/gitlab-adapter.ts
|
|
982
1174
|
init_cjs_shims();
|
|
1175
|
+
var import_common_utils4 = require("@winglet/common-utils");
|
|
983
1176
|
init_executor();
|
|
984
1177
|
var GitLabAdapter = class {
|
|
985
1178
|
platform = "gitlab";
|
|
986
1179
|
scheduler;
|
|
987
1180
|
hostname;
|
|
1181
|
+
defaultBranchCache = null;
|
|
1182
|
+
remoteName;
|
|
1183
|
+
cwd;
|
|
988
1184
|
constructor(options) {
|
|
989
1185
|
this.hostname = options?.hostname ?? "gitlab.com";
|
|
990
1186
|
this.scheduler = options?.scheduler ?? new RequestScheduler();
|
|
1187
|
+
this.remoteName = options?.remoteName ?? "origin";
|
|
1188
|
+
this.cwd = options?.cwd;
|
|
991
1189
|
}
|
|
992
1190
|
async checkAuth() {
|
|
993
1191
|
if (process.env.GITLAB_TOKEN) {
|
|
@@ -997,7 +1195,7 @@ var GitLabAdapter = class {
|
|
|
997
1195
|
const result = await shellExec(
|
|
998
1196
|
"glab",
|
|
999
1197
|
["auth", "status", "--hostname", this.hostname],
|
|
1000
|
-
{ allowExitCodes: [1] }
|
|
1198
|
+
{ cwd: this.cwd, allowExitCodes: [1] }
|
|
1001
1199
|
);
|
|
1002
1200
|
return {
|
|
1003
1201
|
authenticated: result.exitCode === 0,
|
|
@@ -1010,54 +1208,93 @@ var GitLabAdapter = class {
|
|
|
1010
1208
|
async getPRForCommit(sha) {
|
|
1011
1209
|
if (this.scheduler.isRateLimited()) return null;
|
|
1012
1210
|
try {
|
|
1013
|
-
const result = await shellExec(
|
|
1014
|
-
"
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1211
|
+
const result = await shellExec(
|
|
1212
|
+
"glab",
|
|
1213
|
+
[
|
|
1214
|
+
"api",
|
|
1215
|
+
`projects/:id/repository/commits/${sha}/merge_requests`,
|
|
1216
|
+
"--hostname",
|
|
1217
|
+
this.hostname
|
|
1218
|
+
],
|
|
1219
|
+
{ cwd: this.cwd }
|
|
1220
|
+
);
|
|
1019
1221
|
const mrs = JSON.parse(result.stdout);
|
|
1020
|
-
if (!
|
|
1021
|
-
const
|
|
1222
|
+
if (!(0, import_common_utils4.isArray)(mrs) || mrs.length === 0) return null;
|
|
1223
|
+
const mergedMRs = (0, import_common_utils4.filter)(
|
|
1224
|
+
mrs,
|
|
1225
|
+
(mr2) => mr2.state === "merged" && (0, import_common_utils4.isNotNil)(mr2.merged_at)
|
|
1226
|
+
).sort((a, b) => {
|
|
1227
|
+
const aTime = new Date(a.merged_at).getTime();
|
|
1228
|
+
const bTime = new Date(b.merged_at).getTime();
|
|
1229
|
+
return aTime - bTime;
|
|
1230
|
+
});
|
|
1231
|
+
if (mergedMRs.length === 0) return null;
|
|
1232
|
+
const defaultBranch = await this.detectDefaultBranch();
|
|
1233
|
+
const defaultBranchMR = mergedMRs.find(
|
|
1234
|
+
(mr2) => mr2.target_branch === defaultBranch
|
|
1235
|
+
);
|
|
1236
|
+
const mr = defaultBranchMR ?? mergedMRs[0];
|
|
1022
1237
|
return {
|
|
1023
1238
|
number: mr.iid,
|
|
1024
1239
|
title: mr.title ?? "",
|
|
1025
1240
|
author: mr.author?.username ?? "",
|
|
1026
1241
|
url: mr.web_url ?? "",
|
|
1027
1242
|
mergeCommit: mr.merge_commit_sha ?? sha,
|
|
1028
|
-
baseBranch: mr.target_branch ??
|
|
1243
|
+
baseBranch: mr.target_branch ?? defaultBranch,
|
|
1029
1244
|
mergedAt: mr.merged_at
|
|
1030
1245
|
};
|
|
1031
1246
|
} catch {
|
|
1032
1247
|
return null;
|
|
1033
1248
|
}
|
|
1034
1249
|
}
|
|
1250
|
+
async detectDefaultBranch() {
|
|
1251
|
+
if (this.defaultBranchCache) return this.defaultBranchCache;
|
|
1252
|
+
try {
|
|
1253
|
+
const result = await gitExec(
|
|
1254
|
+
["symbolic-ref", `refs/remotes/${this.remoteName}/HEAD`],
|
|
1255
|
+
{ cwd: this.cwd }
|
|
1256
|
+
);
|
|
1257
|
+
const ref = result.stdout.trim();
|
|
1258
|
+
this.defaultBranchCache = ref.replace(`refs/remotes/${this.remoteName}/`, "") || "main";
|
|
1259
|
+
return this.defaultBranchCache;
|
|
1260
|
+
} catch {
|
|
1261
|
+
return "main";
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1035
1264
|
async getPRCommits(prNumber) {
|
|
1036
1265
|
try {
|
|
1037
|
-
const result = await shellExec(
|
|
1038
|
-
"
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1266
|
+
const result = await shellExec(
|
|
1267
|
+
"glab",
|
|
1268
|
+
[
|
|
1269
|
+
"api",
|
|
1270
|
+
`projects/:id/merge_requests/${prNumber}/commits`,
|
|
1271
|
+
"--hostname",
|
|
1272
|
+
this.hostname
|
|
1273
|
+
],
|
|
1274
|
+
{ cwd: this.cwd }
|
|
1275
|
+
);
|
|
1043
1276
|
const commits = JSON.parse(result.stdout);
|
|
1044
|
-
if (!
|
|
1045
|
-
return
|
|
1277
|
+
if (!(0, import_common_utils4.isArray)(commits)) return [];
|
|
1278
|
+
return (0, import_common_utils4.map)(commits, (c) => c.id);
|
|
1046
1279
|
} catch {
|
|
1047
1280
|
return [];
|
|
1048
1281
|
}
|
|
1049
1282
|
}
|
|
1050
1283
|
async getLinkedIssues(prNumber) {
|
|
1051
1284
|
try {
|
|
1052
|
-
const result = await shellExec(
|
|
1053
|
-
"
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1285
|
+
const result = await shellExec(
|
|
1286
|
+
"glab",
|
|
1287
|
+
[
|
|
1288
|
+
"api",
|
|
1289
|
+
`projects/:id/merge_requests/${prNumber}/closes_issues`,
|
|
1290
|
+
"--hostname",
|
|
1291
|
+
this.hostname
|
|
1292
|
+
],
|
|
1293
|
+
{ cwd: this.cwd }
|
|
1294
|
+
);
|
|
1058
1295
|
const issues = JSON.parse(result.stdout);
|
|
1059
|
-
if (!
|
|
1060
|
-
return
|
|
1296
|
+
if (!(0, import_common_utils4.isArray)(issues)) return [];
|
|
1297
|
+
return (0, import_common_utils4.map)(issues, (issue) => ({
|
|
1061
1298
|
number: issue.iid,
|
|
1062
1299
|
title: issue.title ?? "",
|
|
1063
1300
|
url: issue.web_url ?? "",
|
|
@@ -1070,21 +1307,26 @@ var GitLabAdapter = class {
|
|
|
1070
1307
|
}
|
|
1071
1308
|
async getLinkedPRs(issueNumber) {
|
|
1072
1309
|
try {
|
|
1073
|
-
const result = await shellExec(
|
|
1074
|
-
"
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1310
|
+
const result = await shellExec(
|
|
1311
|
+
"glab",
|
|
1312
|
+
[
|
|
1313
|
+
"api",
|
|
1314
|
+
`projects/:id/issues/${issueNumber}/related_merge_requests`,
|
|
1315
|
+
"--hostname",
|
|
1316
|
+
this.hostname
|
|
1317
|
+
],
|
|
1318
|
+
{ cwd: this.cwd }
|
|
1319
|
+
);
|
|
1079
1320
|
const mrs = JSON.parse(result.stdout);
|
|
1080
|
-
if (!
|
|
1081
|
-
|
|
1321
|
+
if (!(0, import_common_utils4.isArray)(mrs)) return [];
|
|
1322
|
+
const defaultBranch = await this.detectDefaultBranch();
|
|
1323
|
+
return (0, import_common_utils4.map)(mrs, (mr) => ({
|
|
1082
1324
|
number: mr.iid,
|
|
1083
1325
|
title: mr.title ?? "",
|
|
1084
1326
|
author: mr.author?.username ?? "",
|
|
1085
1327
|
url: mr.web_url ?? "",
|
|
1086
1328
|
mergeCommit: mr.merge_commit_sha ?? "",
|
|
1087
|
-
baseBranch: mr.target_branch ??
|
|
1329
|
+
baseBranch: mr.target_branch ?? defaultBranch,
|
|
1088
1330
|
mergedAt: mr.merged_at
|
|
1089
1331
|
}));
|
|
1090
1332
|
} catch {
|
|
@@ -1101,7 +1343,12 @@ init_cjs_shims();
|
|
|
1101
1343
|
var GitLabSelfHostedAdapter = class extends GitLabAdapter {
|
|
1102
1344
|
platform = "gitlab-self-hosted";
|
|
1103
1345
|
constructor(hostname, options) {
|
|
1104
|
-
super({
|
|
1346
|
+
super({
|
|
1347
|
+
hostname,
|
|
1348
|
+
scheduler: options?.scheduler,
|
|
1349
|
+
remoteName: options?.remoteName,
|
|
1350
|
+
cwd: options?.cwd
|
|
1351
|
+
});
|
|
1105
1352
|
}
|
|
1106
1353
|
};
|
|
1107
1354
|
|
|
@@ -1110,21 +1357,21 @@ async function detectPlatformAdapter(options) {
|
|
|
1110
1357
|
const remote = await getRemoteInfo(options?.remoteName, {
|
|
1111
1358
|
cwd: options?.cwd
|
|
1112
1359
|
});
|
|
1113
|
-
const adapter = createAdapter(remote);
|
|
1360
|
+
const adapter = createAdapter(remote, options?.remoteName, options?.cwd);
|
|
1114
1361
|
return { adapter, remote };
|
|
1115
1362
|
}
|
|
1116
|
-
function createAdapter(remote) {
|
|
1363
|
+
function createAdapter(remote, remoteName, cwd) {
|
|
1117
1364
|
switch (remote.platform) {
|
|
1118
1365
|
case "github":
|
|
1119
|
-
return new GitHubAdapter({ hostname: remote.host });
|
|
1366
|
+
return new GitHubAdapter({ hostname: remote.host, remoteName, cwd });
|
|
1120
1367
|
case "github-enterprise":
|
|
1121
|
-
return new GitHubEnterpriseAdapter(remote.host);
|
|
1368
|
+
return new GitHubEnterpriseAdapter(remote.host, { remoteName, cwd });
|
|
1122
1369
|
case "gitlab":
|
|
1123
|
-
return new GitLabAdapter({ hostname: remote.host });
|
|
1370
|
+
return new GitLabAdapter({ hostname: remote.host, remoteName, cwd });
|
|
1124
1371
|
case "gitlab-self-hosted":
|
|
1125
|
-
return new GitLabSelfHostedAdapter(remote.host);
|
|
1372
|
+
return new GitLabSelfHostedAdapter(remote.host, { remoteName, cwd });
|
|
1126
1373
|
case "unknown":
|
|
1127
|
-
return new GitHubEnterpriseAdapter(remote.host);
|
|
1374
|
+
return new GitHubEnterpriseAdapter(remote.host, { remoteName, cwd });
|
|
1128
1375
|
}
|
|
1129
1376
|
}
|
|
1130
1377
|
|
|
@@ -1176,6 +1423,7 @@ init_cjs_shims();
|
|
|
1176
1423
|
|
|
1177
1424
|
// src/core/ast-diff/ast-diff.ts
|
|
1178
1425
|
init_cjs_shims();
|
|
1426
|
+
var import_common_utils5 = require("@winglet/common-utils");
|
|
1179
1427
|
init_executor();
|
|
1180
1428
|
|
|
1181
1429
|
// src/core/ast-diff/comparison/index.ts
|
|
@@ -1221,13 +1469,13 @@ function compareSymbolMaps(current, parent) {
|
|
|
1221
1469
|
}
|
|
1222
1470
|
return results;
|
|
1223
1471
|
}
|
|
1224
|
-
function findHashMatch(target,
|
|
1225
|
-
for (const [name, hash] of
|
|
1472
|
+
function findHashMatch(target, map9) {
|
|
1473
|
+
for (const [name, hash] of map9) {
|
|
1226
1474
|
if (hash.exact === target.exact) {
|
|
1227
1475
|
return { name, confidence: "exact" };
|
|
1228
1476
|
}
|
|
1229
1477
|
}
|
|
1230
|
-
for (const [name, hash] of
|
|
1478
|
+
for (const [name, hash] of map9) {
|
|
1231
1479
|
if (hash.structural === target.structural) {
|
|
1232
1480
|
return { name, confidence: "structural" };
|
|
1233
1481
|
}
|
|
@@ -1394,10 +1642,6 @@ var KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
1394
1642
|
// src/core/ast-diff/ast-diff.ts
|
|
1395
1643
|
var MAX_TRAVERSAL_DEPTH = 50;
|
|
1396
1644
|
async function traceByAst(file, line, startCommitSha, options) {
|
|
1397
|
-
if (!isAstAvailable()) {
|
|
1398
|
-
const lang2 = detectLanguage(file);
|
|
1399
|
-
if (!lang2) return null;
|
|
1400
|
-
}
|
|
1401
1645
|
const lang = detectLanguage(file);
|
|
1402
1646
|
if (!lang) return null;
|
|
1403
1647
|
const maxDepth = options?.maxDepth ?? MAX_TRAVERSAL_DEPTH;
|
|
@@ -1421,10 +1665,10 @@ async function traceByAst(file, line, startCommitSha, options) {
|
|
|
1421
1665
|
const parentContent = await getFileAtCommit(parentSha, file, options);
|
|
1422
1666
|
const parentSymbols = await extractSymbols(parentContent, lang);
|
|
1423
1667
|
const currentMap = new Map(
|
|
1424
|
-
[currentSymbol].filter(
|
|
1668
|
+
[currentSymbol].filter(import_common_utils5.isTruthy).map((s) => [s.name, computeContentHash(s.bodyText)])
|
|
1425
1669
|
);
|
|
1426
1670
|
const parentMap = new Map(
|
|
1427
|
-
|
|
1671
|
+
(0, import_common_utils5.map)(parentSymbols, (s) => [s.name, computeContentHash(s.bodyText)])
|
|
1428
1672
|
);
|
|
1429
1673
|
const comparison = compareSymbolMaps(currentMap, parentMap);
|
|
1430
1674
|
if (comparison.length > 0) {
|
|
@@ -1489,6 +1733,7 @@ init_cjs_shims();
|
|
|
1489
1733
|
|
|
1490
1734
|
// src/core/blame/blame.ts
|
|
1491
1735
|
init_cjs_shims();
|
|
1736
|
+
var import_common_utils7 = require("@winglet/common-utils");
|
|
1492
1737
|
init_executor();
|
|
1493
1738
|
|
|
1494
1739
|
// src/core/blame/detection/index.ts
|
|
@@ -1496,6 +1741,7 @@ init_cjs_shims();
|
|
|
1496
1741
|
|
|
1497
1742
|
// src/core/blame/detection/cosmetic-detector.ts
|
|
1498
1743
|
init_cjs_shims();
|
|
1744
|
+
var import_common_utils6 = require("@winglet/common-utils");
|
|
1499
1745
|
init_executor();
|
|
1500
1746
|
function isCosmeticDiff(diff) {
|
|
1501
1747
|
const hunks = extractHunks(diff);
|
|
@@ -1543,23 +1789,33 @@ function normalize(line) {
|
|
|
1543
1789
|
}
|
|
1544
1790
|
function isWhitespaceOnly(hunks) {
|
|
1545
1791
|
return hunks.every((hunk) => {
|
|
1546
|
-
const removedNorm =
|
|
1547
|
-
|
|
1792
|
+
const removedNorm = [];
|
|
1793
|
+
(0, import_common_utils6.forEach)(hunk.removed, (line) => {
|
|
1794
|
+
const n = normalize(line);
|
|
1795
|
+
if ((0, import_common_utils6.isTruthy)(n)) removedNorm.push(n);
|
|
1796
|
+
});
|
|
1797
|
+
removedNorm.sort();
|
|
1798
|
+
const addedNorm = [];
|
|
1799
|
+
(0, import_common_utils6.forEach)(hunk.added, (line) => {
|
|
1800
|
+
const n = normalize(line);
|
|
1801
|
+
if ((0, import_common_utils6.isTruthy)(n)) addedNorm.push(n);
|
|
1802
|
+
});
|
|
1803
|
+
addedNorm.sort();
|
|
1548
1804
|
if (removedNorm.length !== addedNorm.length) return false;
|
|
1549
1805
|
return removedNorm.every((line, idx) => line === addedNorm[idx]);
|
|
1550
1806
|
});
|
|
1551
1807
|
}
|
|
1552
1808
|
function isImportReorder(hunks) {
|
|
1553
1809
|
return hunks.every((hunk) => {
|
|
1554
|
-
const removedImports = hunk.removed
|
|
1555
|
-
const addedImports = hunk.added
|
|
1810
|
+
const removedImports = (0, import_common_utils6.filter)(hunk.removed, isImportLine);
|
|
1811
|
+
const addedImports = (0, import_common_utils6.filter)(hunk.added, isImportLine);
|
|
1556
1812
|
if (removedImports.length === 0) return false;
|
|
1557
|
-
if (removedImports.length !== hunk.removed
|
|
1813
|
+
if (removedImports.length !== (0, import_common_utils6.filter)(hunk.removed, (l) => (0, import_common_utils6.isTruthy)(l.trim())).length)
|
|
1558
1814
|
return false;
|
|
1559
|
-
if (addedImports.length !== hunk.added
|
|
1815
|
+
if (addedImports.length !== (0, import_common_utils6.filter)(hunk.added, (l) => (0, import_common_utils6.isTruthy)(l.trim())).length)
|
|
1560
1816
|
return false;
|
|
1561
|
-
const removedSorted =
|
|
1562
|
-
const addedSorted =
|
|
1817
|
+
const removedSorted = (0, import_common_utils6.map)(removedImports, normalize).sort();
|
|
1818
|
+
const addedSorted = (0, import_common_utils6.map)(addedImports, normalize).sort();
|
|
1563
1819
|
if (removedSorted.length !== addedSorted.length) return false;
|
|
1564
1820
|
return removedSorted.every((line, idx) => line === addedSorted[idx]);
|
|
1565
1821
|
});
|
|
@@ -1655,31 +1911,36 @@ function parsePorcelainOutput(output) {
|
|
|
1655
1911
|
|
|
1656
1912
|
// src/core/blame/blame.ts
|
|
1657
1913
|
async function executeBlame(file, lineRange, options) {
|
|
1658
|
-
const lineSpec =
|
|
1914
|
+
const lineSpec = `${lineRange.start},${lineRange.end}`;
|
|
1659
1915
|
const result = await gitExec(
|
|
1660
1916
|
["blame", "-w", "-C", "-C", "-M", "--porcelain", "-L", lineSpec, file],
|
|
1661
1917
|
options
|
|
1662
1918
|
);
|
|
1663
1919
|
return parsePorcelainOutput(result.stdout);
|
|
1664
1920
|
}
|
|
1665
|
-
async function analyzeBlameResults(results, options) {
|
|
1666
|
-
const uniqueShas = [...new Set(
|
|
1921
|
+
async function analyzeBlameResults(results, filePath, options) {
|
|
1922
|
+
const uniqueShas = [...new Set((0, import_common_utils7.map)(results, (r) => r.commitHash))];
|
|
1667
1923
|
const cosmeticMap = /* @__PURE__ */ new Map();
|
|
1668
1924
|
const zeroSha = "0".repeat(40);
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1925
|
+
const tasks = [];
|
|
1926
|
+
(0, import_common_utils7.forEach)(uniqueShas, (sha) => {
|
|
1927
|
+
if (sha === zeroSha) return;
|
|
1928
|
+
tasks.push(
|
|
1929
|
+
(async () => {
|
|
1930
|
+
try {
|
|
1931
|
+
const blameResult = results.find((r) => r.commitHash === sha);
|
|
1932
|
+
if (!blameResult) return;
|
|
1933
|
+
const file = blameResult.originalFile ?? filePath;
|
|
1934
|
+
const diff = await getCosmeticDiff(sha, file, options);
|
|
1935
|
+
cosmeticMap.set(sha, isCosmeticDiff(diff));
|
|
1936
|
+
} catch {
|
|
1937
|
+
cosmeticMap.set(sha, { isCosmetic: false });
|
|
1938
|
+
}
|
|
1939
|
+
})()
|
|
1940
|
+
);
|
|
1941
|
+
});
|
|
1942
|
+
await Promise.all(tasks);
|
|
1943
|
+
return (0, import_common_utils7.map)(results, (blame) => {
|
|
1683
1944
|
const cosmetic = cosmeticMap.get(blame.commitHash);
|
|
1684
1945
|
return {
|
|
1685
1946
|
blame,
|
|
@@ -1689,6 +1950,102 @@ async function analyzeBlameResults(results, options) {
|
|
|
1689
1950
|
});
|
|
1690
1951
|
}
|
|
1691
1952
|
|
|
1953
|
+
// src/core/issue-graph/index.ts
|
|
1954
|
+
init_cjs_shims();
|
|
1955
|
+
|
|
1956
|
+
// src/core/issue-graph/issue-graph.ts
|
|
1957
|
+
init_cjs_shims();
|
|
1958
|
+
var import_common_utils8 = require("@winglet/common-utils");
|
|
1959
|
+
var DEFAULT_MAX_DEPTH = 2;
|
|
1960
|
+
async function traverseIssueGraph(adapter, startType, startNumber, options) {
|
|
1961
|
+
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
1962
|
+
const nodes = [];
|
|
1963
|
+
const edges = [];
|
|
1964
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1965
|
+
await traverse(
|
|
1966
|
+
adapter,
|
|
1967
|
+
startType,
|
|
1968
|
+
startNumber,
|
|
1969
|
+
0,
|
|
1970
|
+
maxDepth,
|
|
1971
|
+
nodes,
|
|
1972
|
+
edges,
|
|
1973
|
+
visited
|
|
1974
|
+
);
|
|
1975
|
+
return { nodes, edges };
|
|
1976
|
+
}
|
|
1977
|
+
async function traverse(adapter, type, number, depth, maxDepth, nodes, edges, visited) {
|
|
1978
|
+
const key = `${type}:${number}`;
|
|
1979
|
+
if (visited.has(key)) return;
|
|
1980
|
+
if (depth > maxDepth) return;
|
|
1981
|
+
visited.add(key);
|
|
1982
|
+
if (type === "pr") {
|
|
1983
|
+
nodes.push({
|
|
1984
|
+
type: "pull_request",
|
|
1985
|
+
trackingMethod: "issue-link",
|
|
1986
|
+
confidence: "exact",
|
|
1987
|
+
prNumber: number
|
|
1988
|
+
});
|
|
1989
|
+
if (depth < maxDepth) {
|
|
1990
|
+
const linkedIssues = await adapter.getLinkedIssues(number);
|
|
1991
|
+
for (const issue of linkedIssues) {
|
|
1992
|
+
edges.push({
|
|
1993
|
+
from: `pr:${number}`,
|
|
1994
|
+
to: `issue:${issue.number}`,
|
|
1995
|
+
relation: "closes"
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
await Promise.all(
|
|
1999
|
+
(0, import_common_utils8.map)(
|
|
2000
|
+
linkedIssues,
|
|
2001
|
+
(issue) => traverse(
|
|
2002
|
+
adapter,
|
|
2003
|
+
"issue",
|
|
2004
|
+
issue.number,
|
|
2005
|
+
depth + 1,
|
|
2006
|
+
maxDepth,
|
|
2007
|
+
nodes,
|
|
2008
|
+
edges,
|
|
2009
|
+
visited
|
|
2010
|
+
)
|
|
2011
|
+
)
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
} else {
|
|
2015
|
+
nodes.push({
|
|
2016
|
+
type: "issue",
|
|
2017
|
+
trackingMethod: "issue-link",
|
|
2018
|
+
confidence: "exact",
|
|
2019
|
+
issueNumber: number
|
|
2020
|
+
});
|
|
2021
|
+
if (depth < maxDepth) {
|
|
2022
|
+
const linkedPRs = await adapter.getLinkedPRs(number);
|
|
2023
|
+
for (const pr of linkedPRs) {
|
|
2024
|
+
edges.push({
|
|
2025
|
+
from: `issue:${number}`,
|
|
2026
|
+
to: `pr:${pr.number}`,
|
|
2027
|
+
relation: "referenced-by"
|
|
2028
|
+
});
|
|
2029
|
+
}
|
|
2030
|
+
await Promise.all(
|
|
2031
|
+
(0, import_common_utils8.map)(
|
|
2032
|
+
linkedPRs,
|
|
2033
|
+
(pr) => traverse(
|
|
2034
|
+
adapter,
|
|
2035
|
+
"pr",
|
|
2036
|
+
pr.number,
|
|
2037
|
+
depth + 1,
|
|
2038
|
+
maxDepth,
|
|
2039
|
+
nodes,
|
|
2040
|
+
edges,
|
|
2041
|
+
visited
|
|
2042
|
+
)
|
|
2043
|
+
)
|
|
2044
|
+
);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
|
|
1692
2049
|
// src/core/core.ts
|
|
1693
2050
|
init_pr_lookup2();
|
|
1694
2051
|
function computeFeatureFlags(operatingLevel, options) {
|
|
@@ -1696,24 +2053,35 @@ function computeFeatureFlags(operatingLevel, options) {
|
|
|
1696
2053
|
astDiff: isAstAvailable() && !options.noAst,
|
|
1697
2054
|
deepTrace: operatingLevel === 2 && (options.deep ?? false),
|
|
1698
2055
|
commitGraph: false,
|
|
1699
|
-
issueGraph: operatingLevel === 2 && (options.graphDepth ?? 0) > 0,
|
|
1700
2056
|
graphql: operatingLevel === 2
|
|
1701
2057
|
};
|
|
1702
2058
|
}
|
|
2059
|
+
async function resolveRepoIdentity(cwd) {
|
|
2060
|
+
try {
|
|
2061
|
+
const result = await gitExec(["rev-parse", "--show-toplevel"], { cwd });
|
|
2062
|
+
const hash = (0, import_node_crypto2.createHash)("sha256").update(result.stdout.trim()).digest("hex").slice(0, 16);
|
|
2063
|
+
return { host: "_local", owner: "_", repo: hash };
|
|
2064
|
+
} catch {
|
|
2065
|
+
return { host: "_local", owner: "_", repo: "_unknown" };
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
1703
2068
|
async function detectPlatform2(options) {
|
|
1704
2069
|
const warnings = [];
|
|
1705
2070
|
let adapter = null;
|
|
2071
|
+
let remote = null;
|
|
1706
2072
|
let operatingLevel = 0;
|
|
1707
2073
|
try {
|
|
1708
|
-
const
|
|
1709
|
-
remoteName: options.remote
|
|
2074
|
+
const detected = await detectPlatformAdapter({
|
|
2075
|
+
remoteName: options.remote,
|
|
2076
|
+
cwd: options.cwd
|
|
1710
2077
|
});
|
|
1711
|
-
adapter =
|
|
2078
|
+
adapter = detected.adapter;
|
|
2079
|
+
remote = detected.remote;
|
|
1712
2080
|
} catch {
|
|
1713
2081
|
operatingLevel = 0;
|
|
1714
2082
|
warnings.push("Could not detect platform. Running in Level 0 (git only).");
|
|
1715
2083
|
}
|
|
1716
|
-
return { adapter, operatingLevel, warnings };
|
|
2084
|
+
return { adapter, remote, operatingLevel, warnings };
|
|
1717
2085
|
}
|
|
1718
2086
|
async function runBlameAndAuth(adapter, options, execOptions) {
|
|
1719
2087
|
const warnings = [];
|
|
@@ -1721,7 +2089,7 @@ async function runBlameAndAuth(adapter, options, execOptions) {
|
|
|
1721
2089
|
options.endLine ? `${options.line},${options.endLine}` : `${options.line}`
|
|
1722
2090
|
);
|
|
1723
2091
|
const blameChain = executeBlame(options.file, lineRange, execOptions).then(
|
|
1724
|
-
(results) => analyzeBlameResults(results, execOptions)
|
|
2092
|
+
(results) => analyzeBlameResults(results, options.file, execOptions)
|
|
1725
2093
|
);
|
|
1726
2094
|
const [authResult, blameResult] = await Promise.allSettled([
|
|
1727
2095
|
adapter ? adapter.checkAuth() : Promise.resolve({ authenticated: false }),
|
|
@@ -1743,55 +2111,83 @@ async function runBlameAndAuth(adapter, options, execOptions) {
|
|
|
1743
2111
|
}
|
|
1744
2112
|
return { analyzed: blameResult.value, operatingLevel, warnings };
|
|
1745
2113
|
}
|
|
1746
|
-
async function
|
|
2114
|
+
async function processEntry(entry, featureFlags, adapter, options, execOptions, repoId) {
|
|
1747
2115
|
const nodes = [];
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
});
|
|
1771
|
-
}
|
|
2116
|
+
const commitNode = {
|
|
2117
|
+
type: entry.isCosmetic ? "cosmetic_commit" : "original_commit",
|
|
2118
|
+
sha: entry.blame.commitHash,
|
|
2119
|
+
trackingMethod: "blame-CMw",
|
|
2120
|
+
confidence: "exact",
|
|
2121
|
+
note: entry.cosmeticReason ? `Cosmetic change: ${entry.cosmeticReason}` : void 0
|
|
2122
|
+
};
|
|
2123
|
+
nodes.push(commitNode);
|
|
2124
|
+
if (entry.isCosmetic && featureFlags.astDiff) {
|
|
2125
|
+
const astResult = await traceByAst(
|
|
2126
|
+
options.file,
|
|
2127
|
+
options.line,
|
|
2128
|
+
entry.blame.commitHash,
|
|
2129
|
+
execOptions
|
|
2130
|
+
);
|
|
2131
|
+
if (astResult) {
|
|
2132
|
+
nodes.push({
|
|
2133
|
+
type: "original_commit",
|
|
2134
|
+
sha: astResult.originSha,
|
|
2135
|
+
trackingMethod: "ast-signature",
|
|
2136
|
+
confidence: astResult.confidence
|
|
2137
|
+
});
|
|
1772
2138
|
}
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
2139
|
+
}
|
|
2140
|
+
const targetSha = nodes[nodes.length - 1].sha;
|
|
2141
|
+
if (targetSha) {
|
|
2142
|
+
const prInfo = await lookupPR(targetSha, adapter, {
|
|
2143
|
+
...execOptions,
|
|
2144
|
+
noCache: options.noCache,
|
|
2145
|
+
deep: featureFlags.deepTrace,
|
|
2146
|
+
repoId
|
|
2147
|
+
});
|
|
2148
|
+
if (prInfo) {
|
|
2149
|
+
nodes.push({
|
|
2150
|
+
type: "pull_request",
|
|
2151
|
+
sha: prInfo.mergeCommit,
|
|
2152
|
+
trackingMethod: prInfo.url ? "api" : "message-parse",
|
|
2153
|
+
confidence: prInfo.url ? "exact" : "heuristic",
|
|
2154
|
+
prNumber: prInfo.number,
|
|
2155
|
+
prUrl: prInfo.url || void 0,
|
|
2156
|
+
prTitle: prInfo.title || void 0,
|
|
2157
|
+
mergedAt: prInfo.mergedAt
|
|
2158
|
+
});
|
|
1788
2159
|
}
|
|
1789
2160
|
}
|
|
1790
2161
|
return nodes;
|
|
1791
2162
|
}
|
|
2163
|
+
async function buildTraceNodes(analyzed, featureFlags, adapter, options, execOptions, repoId) {
|
|
2164
|
+
const results = await Promise.allSettled(
|
|
2165
|
+
(0, import_common_utils11.map)(
|
|
2166
|
+
analyzed,
|
|
2167
|
+
(entry) => processEntry(entry, featureFlags, adapter, options, execOptions, repoId)
|
|
2168
|
+
)
|
|
2169
|
+
);
|
|
2170
|
+
return results.flatMap((r) => r.status === "fulfilled" ? r.value : []);
|
|
2171
|
+
}
|
|
2172
|
+
var legacyCacheCleaned = false;
|
|
1792
2173
|
async function trace(options) {
|
|
1793
|
-
const execOptions = { cwd:
|
|
2174
|
+
const execOptions = { cwd: options.cwd };
|
|
2175
|
+
if (!legacyCacheCleaned) {
|
|
2176
|
+
legacyCacheCleaned = true;
|
|
2177
|
+
cleanupLegacyCache().catch(() => {
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
1794
2180
|
const platform = await detectPlatform2(options);
|
|
2181
|
+
let repoId;
|
|
2182
|
+
if (platform.remote) {
|
|
2183
|
+
repoId = {
|
|
2184
|
+
host: platform.remote.host,
|
|
2185
|
+
owner: platform.remote.owner,
|
|
2186
|
+
repo: platform.remote.repo
|
|
2187
|
+
};
|
|
2188
|
+
} else {
|
|
2189
|
+
repoId = await resolveRepoIdentity(options.cwd);
|
|
2190
|
+
}
|
|
1795
2191
|
const blameAuth = await runBlameAndAuth(
|
|
1796
2192
|
platform.adapter,
|
|
1797
2193
|
options,
|
|
@@ -1805,10 +2201,26 @@ async function trace(options) {
|
|
|
1805
2201
|
featureFlags,
|
|
1806
2202
|
platform.adapter,
|
|
1807
2203
|
options,
|
|
1808
|
-
execOptions
|
|
2204
|
+
execOptions,
|
|
2205
|
+
repoId
|
|
1809
2206
|
);
|
|
1810
2207
|
return { nodes, operatingLevel, featureFlags, warnings };
|
|
1811
2208
|
}
|
|
2209
|
+
async function graph(options) {
|
|
2210
|
+
const { adapter } = await detectPlatformAdapter({
|
|
2211
|
+
remoteName: options.remote
|
|
2212
|
+
});
|
|
2213
|
+
const auth = await adapter.checkAuth();
|
|
2214
|
+
if (!auth.authenticated) {
|
|
2215
|
+
throw new LineLoreError(
|
|
2216
|
+
LineLoreErrorCode.CLI_NOT_AUTHENTICATED,
|
|
2217
|
+
'Platform CLI is not authenticated. Run "gh auth login" or set the appropriate token.'
|
|
2218
|
+
);
|
|
2219
|
+
}
|
|
2220
|
+
return traverseIssueGraph(adapter, options.type, options.number, {
|
|
2221
|
+
maxDepth: options.depth
|
|
2222
|
+
});
|
|
2223
|
+
}
|
|
1812
2224
|
async function health(options) {
|
|
1813
2225
|
const healthReport = await checkGitHealth(options);
|
|
1814
2226
|
let operatingLevel = 0;
|
|
@@ -1822,94 +2234,19 @@ async function health(options) {
|
|
|
1822
2234
|
return { ...healthReport, operatingLevel };
|
|
1823
2235
|
}
|
|
1824
2236
|
async function clearCache() {
|
|
2237
|
+
const { rm: rm2 } = await import("fs/promises");
|
|
2238
|
+
const { homedir: homedir2 } = await import("os");
|
|
2239
|
+
const { join: join2 } = await import("path");
|
|
1825
2240
|
const { resetPRCache: resetPRCache2 } = await Promise.resolve().then(() => (init_pr_lookup2(), pr_lookup_exports));
|
|
1826
2241
|
const { resetPatchIdCache: resetPatchIdCache2 } = await Promise.resolve().then(() => (init_patch_id2(), patch_id_exports));
|
|
1827
2242
|
resetPRCache2();
|
|
1828
2243
|
resetPatchIdCache2();
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
// src/core/issue-graph/issue-graph.ts
|
|
1835
|
-
init_cjs_shims();
|
|
1836
|
-
var DEFAULT_MAX_DEPTH = 2;
|
|
1837
|
-
async function traverseIssueGraph(adapter, startType, startNumber, options) {
|
|
1838
|
-
const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
1839
|
-
const nodes = [];
|
|
1840
|
-
const edges = [];
|
|
1841
|
-
const visited = /* @__PURE__ */ new Set();
|
|
1842
|
-
await traverse(
|
|
1843
|
-
adapter,
|
|
1844
|
-
startType,
|
|
1845
|
-
startNumber,
|
|
1846
|
-
0,
|
|
1847
|
-
maxDepth,
|
|
1848
|
-
nodes,
|
|
1849
|
-
edges,
|
|
1850
|
-
visited
|
|
1851
|
-
);
|
|
1852
|
-
return { nodes, edges };
|
|
1853
|
-
}
|
|
1854
|
-
async function traverse(adapter, type, number, depth, maxDepth, nodes, edges, visited) {
|
|
1855
|
-
const key = `${type}:${number}`;
|
|
1856
|
-
if (visited.has(key)) return;
|
|
1857
|
-
if (depth > maxDepth) return;
|
|
1858
|
-
visited.add(key);
|
|
1859
|
-
if (type === "pr") {
|
|
1860
|
-
nodes.push({
|
|
1861
|
-
type: "pull_request",
|
|
1862
|
-
trackingMethod: "issue-link",
|
|
1863
|
-
confidence: "exact",
|
|
1864
|
-
prNumber: number
|
|
1865
|
-
});
|
|
1866
|
-
if (depth < maxDepth) {
|
|
1867
|
-
const linkedIssues = await adapter.getLinkedIssues(number);
|
|
1868
|
-
for (const issue of linkedIssues) {
|
|
1869
|
-
edges.push({
|
|
1870
|
-
from: `pr:${number}`,
|
|
1871
|
-
to: `issue:${issue.number}`,
|
|
1872
|
-
relation: "closes"
|
|
1873
|
-
});
|
|
1874
|
-
await traverse(
|
|
1875
|
-
adapter,
|
|
1876
|
-
"issue",
|
|
1877
|
-
issue.number,
|
|
1878
|
-
depth + 1,
|
|
1879
|
-
maxDepth,
|
|
1880
|
-
nodes,
|
|
1881
|
-
edges,
|
|
1882
|
-
visited
|
|
1883
|
-
);
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
} else {
|
|
1887
|
-
nodes.push({
|
|
1888
|
-
type: "issue",
|
|
1889
|
-
trackingMethod: "issue-link",
|
|
1890
|
-
confidence: "exact",
|
|
1891
|
-
issueNumber: number
|
|
2244
|
+
try {
|
|
2245
|
+
await rm2(join2(homedir2(), ".line-lore", "cache"), {
|
|
2246
|
+
recursive: true,
|
|
2247
|
+
force: true
|
|
1892
2248
|
});
|
|
1893
|
-
|
|
1894
|
-
const linkedPRs = await adapter.getLinkedPRs(number);
|
|
1895
|
-
for (const pr of linkedPRs) {
|
|
1896
|
-
edges.push({
|
|
1897
|
-
from: `issue:${number}`,
|
|
1898
|
-
to: `pr:${pr.number}`,
|
|
1899
|
-
relation: "referenced-by"
|
|
1900
|
-
});
|
|
1901
|
-
await traverse(
|
|
1902
|
-
adapter,
|
|
1903
|
-
"pr",
|
|
1904
|
-
pr.number,
|
|
1905
|
-
depth + 1,
|
|
1906
|
-
maxDepth,
|
|
1907
|
-
nodes,
|
|
1908
|
-
edges,
|
|
1909
|
-
visited
|
|
1910
|
-
);
|
|
1911
|
-
}
|
|
1912
|
-
}
|
|
2249
|
+
} catch {
|
|
1913
2250
|
}
|
|
1914
2251
|
}
|
|
1915
2252
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -1917,6 +2254,7 @@ async function traverse(adapter, type, number, depth, maxDepth, nodes, edges, vi
|
|
|
1917
2254
|
LineLoreError,
|
|
1918
2255
|
LineLoreErrorCode,
|
|
1919
2256
|
clearCache,
|
|
2257
|
+
graph,
|
|
1920
2258
|
health,
|
|
1921
2259
|
trace,
|
|
1922
2260
|
traverseIssueGraph
|