@mneme-ai/core 0.8.3
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 +31 -0
- package/dist/correlate/index.d.ts +44 -0
- package/dist/correlate/index.d.ts.map +1 -0
- package/dist/correlate/index.js +21 -0
- package/dist/correlate/index.js.map +1 -0
- package/dist/enrich/index.d.ts +35 -0
- package/dist/enrich/index.d.ts.map +1 -0
- package/dist/enrich/index.js +69 -0
- package/dist/enrich/index.js.map +1 -0
- package/dist/entities/cosine-clones.d.ts +6 -0
- package/dist/entities/cosine-clones.d.ts.map +1 -0
- package/dist/entities/cosine-clones.js +142 -0
- package/dist/entities/cosine-clones.js.map +1 -0
- package/dist/entities/cosine-clones.test.d.ts +2 -0
- package/dist/entities/cosine-clones.test.d.ts.map +1 -0
- package/dist/entities/cosine-clones.test.js +109 -0
- package/dist/entities/cosine-clones.test.js.map +1 -0
- package/dist/entities/index.d.ts +74 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +24 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/entities/python-parser.d.ts +16 -0
- package/dist/entities/python-parser.d.ts.map +1 -0
- package/dist/entities/python-parser.js +248 -0
- package/dist/entities/python-parser.js.map +1 -0
- package/dist/entities/typescript-parser.d.ts +18 -0
- package/dist/entities/typescript-parser.d.ts.map +1 -0
- package/dist/entities/typescript-parser.js +220 -0
- package/dist/entities/typescript-parser.js.map +1 -0
- package/dist/entities/typescript-parser.test.d.ts +2 -0
- package/dist/entities/typescript-parser.test.d.ts.map +1 -0
- package/dist/entities/typescript-parser.test.js +103 -0
- package/dist/entities/typescript-parser.test.js.map +1 -0
- package/dist/git/blame.d.ts +9 -0
- package/dist/git/blame.d.ts.map +1 -0
- package/dist/git/blame.js +56 -0
- package/dist/git/blame.js.map +1 -0
- package/dist/git/exec.d.ts +13 -0
- package/dist/git/exec.d.ts.map +1 -0
- package/dist/git/exec.js +40 -0
- package/dist/git/exec.js.map +1 -0
- package/dist/git/github.d.ts +62 -0
- package/dist/git/github.d.ts.map +1 -0
- package/dist/git/github.js +115 -0
- package/dist/git/github.js.map +1 -0
- package/dist/git/github.test.d.ts +2 -0
- package/dist/git/github.test.d.ts.map +1 -0
- package/dist/git/github.test.js +86 -0
- package/dist/git/github.test.js.map +1 -0
- package/dist/git/gitlab.d.ts +66 -0
- package/dist/git/gitlab.d.ts.map +1 -0
- package/dist/git/gitlab.js +121 -0
- package/dist/git/gitlab.js.map +1 -0
- package/dist/git/gitlab.test.d.ts +2 -0
- package/dist/git/gitlab.test.d.ts.map +1 -0
- package/dist/git/gitlab.test.js +122 -0
- package/dist/git/gitlab.test.js.map +1 -0
- package/dist/git/index.d.ts +7 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +7 -0
- package/dist/git/index.js.map +1 -0
- package/dist/git/log.d.ts +11 -0
- package/dist/git/log.d.ts.map +1 -0
- package/dist/git/log.js +107 -0
- package/dist/git/log.js.map +1 -0
- package/dist/git/log.test.d.ts +2 -0
- package/dist/git/log.test.d.ts.map +1 -0
- package/dist/git/log.test.js +88 -0
- package/dist/git/log.test.js.map +1 -0
- package/dist/git/repo.d.ts +12 -0
- package/dist/git/repo.d.ts.map +1 -0
- package/dist/git/repo.js +50 -0
- package/dist/git/repo.js.map +1 -0
- package/dist/git/repo.test.d.ts +2 -0
- package/dist/git/repo.test.d.ts.map +1 -0
- package/dist/git/repo.test.js +35 -0
- package/dist/git/repo.test.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/index.d.ts +2 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +2 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/indexer.d.ts +22 -0
- package/dist/indexer/indexer.d.ts.map +1 -0
- package/dist/indexer/indexer.js +107 -0
- package/dist/indexer/indexer.js.map +1 -0
- package/dist/indexer/indexer.test.d.ts +2 -0
- package/dist/indexer/indexer.test.d.ts.map +1 -0
- package/dist/indexer/indexer.test.js +80 -0
- package/dist/indexer/indexer.test.js.map +1 -0
- package/dist/retrieve/index.d.ts +3 -0
- package/dist/retrieve/index.d.ts.map +1 -0
- package/dist/retrieve/index.js +3 -0
- package/dist/retrieve/index.js.map +1 -0
- package/dist/retrieve/rerank.d.ts +44 -0
- package/dist/retrieve/rerank.d.ts.map +1 -0
- package/dist/retrieve/rerank.js +68 -0
- package/dist/retrieve/rerank.js.map +1 -0
- package/dist/retrieve/rerank.test.d.ts +2 -0
- package/dist/retrieve/rerank.test.d.ts.map +1 -0
- package/dist/retrieve/rerank.test.js +52 -0
- package/dist/retrieve/rerank.test.js.map +1 -0
- package/dist/retrieve/search.d.ts +31 -0
- package/dist/retrieve/search.d.ts.map +1 -0
- package/dist/retrieve/search.js +170 -0
- package/dist/retrieve/search.js.map +1 -0
- package/dist/retrieve/search.test.d.ts +2 -0
- package/dist/retrieve/search.test.d.ts.map +1 -0
- package/dist/retrieve/search.test.js +105 -0
- package/dist/retrieve/search.test.js.map +1 -0
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +3 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/schema.d.ts +11 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +129 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/sqlite.d.ts +51 -0
- package/dist/store/sqlite.d.ts.map +1 -0
- package/dist/store/sqlite.js +262 -0
- package/dist/store/sqlite.js.map +1 -0
- package/dist/store/sqlite.test.d.ts +2 -0
- package/dist/store/sqlite.test.d.ts.map +1 -0
- package/dist/store/sqlite.test.js +128 -0
- package/dist/store/sqlite.test.js.map +1 -0
- package/dist/types.d.ts +115 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/util/index.d.ts +15 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +65 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/index.test.d.ts +2 -0
- package/dist/util/index.test.d.ts.map +1 -0
- package/dist/util/index.test.js +37 -0
- package/dist/util/index.test.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { execGit } from "./exec.js";
|
|
2
|
+
export async function blame(cwd, filePath, startLine, endLine) {
|
|
3
|
+
const args = ["blame", "--porcelain"];
|
|
4
|
+
if (startLine && endLine)
|
|
5
|
+
args.push("-L", `${startLine},${endLine}`);
|
|
6
|
+
args.push(filePath);
|
|
7
|
+
const r = await execGit(args, { cwd });
|
|
8
|
+
if (r.code !== 0)
|
|
9
|
+
return [];
|
|
10
|
+
return parseBlamePorcelain(r.stdout);
|
|
11
|
+
}
|
|
12
|
+
function parseBlamePorcelain(raw) {
|
|
13
|
+
const lines = raw.split("\n");
|
|
14
|
+
const commitMeta = new Map();
|
|
15
|
+
const result = [];
|
|
16
|
+
let i = 0;
|
|
17
|
+
while (i < lines.length) {
|
|
18
|
+
const header = lines[i];
|
|
19
|
+
if (!header) {
|
|
20
|
+
i++;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const m = header.match(/^([0-9a-f]{40}) \d+ (\d+)(?: \d+)?$/);
|
|
24
|
+
if (!m) {
|
|
25
|
+
i++;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const hash = m[1];
|
|
29
|
+
const lineNum = Number(m[2]);
|
|
30
|
+
let meta = commitMeta.get(hash);
|
|
31
|
+
if (!meta) {
|
|
32
|
+
meta = {};
|
|
33
|
+
commitMeta.set(hash, meta);
|
|
34
|
+
}
|
|
35
|
+
i++;
|
|
36
|
+
while (i < lines.length && !lines[i].startsWith("\t")) {
|
|
37
|
+
const ln = lines[i];
|
|
38
|
+
if (ln.startsWith("author "))
|
|
39
|
+
meta.author = ln.slice(7);
|
|
40
|
+
else if (ln.startsWith("author-time "))
|
|
41
|
+
meta.authorTime = Number(ln.slice(12));
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
const content = lines[i]?.slice(1) ?? "";
|
|
45
|
+
result.push({
|
|
46
|
+
commitHash: hash,
|
|
47
|
+
authorName: meta.author ?? "",
|
|
48
|
+
authorTime: meta.authorTime ?? 0,
|
|
49
|
+
lineNumber: lineNum,
|
|
50
|
+
content,
|
|
51
|
+
});
|
|
52
|
+
i++;
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=blame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blame.js","sourceRoot":"","sources":["../../src/git/blame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,GAAW,EACX,QAAgB,EAChB,SAAkB,EAClB,OAAgB;IAEhB,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IACtC,IAAI,SAAS,IAAI,OAAO;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,SAAS,IAAI,OAAO,EAAE,CAAC,CAAC;IACrE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEpB,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5B,OAAO,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoD,CAAC;IAC/E,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QAC9D,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,CAAC,EAAE,CAAC;YACJ,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACnB,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,GAAG,EAAE,CAAC;YACV,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,CAAC,EAAE,CAAC;QACJ,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YACrB,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;gBAAE,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;iBACnD,IAAI,EAAE,CAAC,UAAU,CAAC,cAAc,CAAC;gBAAE,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;YAC/E,CAAC,EAAE,CAAC;QACN,CAAC;QACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC;YACV,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;YAC7B,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC;YAChC,UAAU,EAAE,OAAO;YACnB,OAAO;SACR,CAAC,CAAC;QACH,CAAC,EAAE,CAAC;IACN,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ExecOptions {
|
|
2
|
+
cwd: string;
|
|
3
|
+
maxBuffer?: number;
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
}
|
|
6
|
+
export interface ExecResult {
|
|
7
|
+
stdout: string;
|
|
8
|
+
stderr: string;
|
|
9
|
+
code: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function execGit(args: string[], opts: ExecOptions): Promise<ExecResult>;
|
|
12
|
+
export declare function execGitOk(args: string[], opts: ExecOptions): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/git/exec.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAgCpF;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlF"}
|
package/dist/git/exec.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export async function execGit(args, opts) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
const child = spawn("git", args, {
|
|
5
|
+
cwd: opts.cwd,
|
|
6
|
+
env: opts.env ?? process.env,
|
|
7
|
+
windowsHide: true,
|
|
8
|
+
});
|
|
9
|
+
const stdout = [];
|
|
10
|
+
const stderr = [];
|
|
11
|
+
let stdoutSize = 0;
|
|
12
|
+
const maxBuffer = opts.maxBuffer ?? 256 * 1024 * 1024;
|
|
13
|
+
child.stdout.on("data", (chunk) => {
|
|
14
|
+
stdoutSize += chunk.length;
|
|
15
|
+
if (stdoutSize > maxBuffer) {
|
|
16
|
+
child.kill();
|
|
17
|
+
reject(new Error(`git ${args[0]} exceeded max buffer (${maxBuffer} bytes)`));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
stdout.push(chunk);
|
|
21
|
+
});
|
|
22
|
+
child.stderr.on("data", (chunk) => stderr.push(chunk));
|
|
23
|
+
child.on("error", reject);
|
|
24
|
+
child.on("close", (code) => {
|
|
25
|
+
resolve({
|
|
26
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
27
|
+
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
28
|
+
code: code ?? 0,
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export async function execGitOk(args, opts) {
|
|
34
|
+
const r = await execGit(args, opts);
|
|
35
|
+
if (r.code !== 0) {
|
|
36
|
+
throw new Error(`git ${args.join(" ")} failed (exit ${r.code}): ${r.stderr.trim()}`);
|
|
37
|
+
}
|
|
38
|
+
return r.stdout;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/git/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAc3C,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAc,EAAE,IAAiB;IAC7D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE;YAC/B,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG;YAC5B,WAAW,EAAE,IAAI;SAClB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;QAEtD,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC;YAC3B,IAAI,UAAU,GAAG,SAAS,EAAE,CAAC;gBAC3B,KAAK,CAAC,IAAI,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,yBAAyB,SAAS,SAAS,CAAC,CAAC,CAAC;gBAC7E,OAAO;YACT,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC9C,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAC9C,IAAI,EAAE,IAAI,IAAI,CAAC;aAChB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc,EAAE,IAAiB;IAC/D,MAAM,CAAC,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,CAAC,CAAC,MAAM,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub PR + issue body fetcher.
|
|
3
|
+
*
|
|
4
|
+
* The single biggest accuracy improvement for archeology questions: most of
|
|
5
|
+
* the WHY behind a commit lives in the PR description, not the commit message.
|
|
6
|
+
* This module hydrates each commit's pr_title / pr_body / issue_body fields
|
|
7
|
+
* by calling the GitHub REST API.
|
|
8
|
+
*
|
|
9
|
+
* Auth: read GITHUB_TOKEN from env, or accept it via opts.token. The token
|
|
10
|
+
* is never persisted; it lives in process memory only.
|
|
11
|
+
*
|
|
12
|
+
* Rate limiting: GitHub allows 5,000 req/hr authenticated, 60/hr anonymous.
|
|
13
|
+
* We respect Retry-After + use exponential backoff on 429/5xx.
|
|
14
|
+
*/
|
|
15
|
+
import type { Commit } from "../types.js";
|
|
16
|
+
export interface GitHubFetchOptions {
|
|
17
|
+
owner: string;
|
|
18
|
+
repo: string;
|
|
19
|
+
/** Personal access token. If absent, uses anonymous (60 req/hr cap). */
|
|
20
|
+
token?: string;
|
|
21
|
+
/** Override base URL for GitHub Enterprise. Default https://api.github.com. */
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
/** Concurrent request cap. Default 4. */
|
|
24
|
+
concurrency?: number;
|
|
25
|
+
/** Per-request timeout (ms). Default 15000. */
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
/** Optional progress callback. */
|
|
28
|
+
onProgress?: (done: number, total: number) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface PullRequestInfo {
|
|
31
|
+
number: number;
|
|
32
|
+
title: string;
|
|
33
|
+
body: string;
|
|
34
|
+
url: string;
|
|
35
|
+
state: string;
|
|
36
|
+
mergedAt?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface IssueInfo {
|
|
39
|
+
number: number;
|
|
40
|
+
title: string;
|
|
41
|
+
body: string;
|
|
42
|
+
url: string;
|
|
43
|
+
state: string;
|
|
44
|
+
}
|
|
45
|
+
export declare class GitHubAdapter {
|
|
46
|
+
private readonly base;
|
|
47
|
+
private readonly token?;
|
|
48
|
+
private readonly concurrency;
|
|
49
|
+
private readonly timeoutMs;
|
|
50
|
+
constructor(opts: GitHubFetchOptions);
|
|
51
|
+
private readonly owner;
|
|
52
|
+
private readonly repo;
|
|
53
|
+
fetchPullRequest(num: number): Promise<PullRequestInfo | null>;
|
|
54
|
+
fetchIssue(num: number): Promise<IssueInfo | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Hydrate an array of commits with PR title/body in place.
|
|
57
|
+
* Skips commits that already have prBody, or have no prNumber.
|
|
58
|
+
*/
|
|
59
|
+
hydrateCommits(commits: Commit[], onProgress?: (done: number, total: number) => void): Promise<void>;
|
|
60
|
+
private request;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/git/github.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAID,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,IAAI,EAAE,kBAAkB;IAYpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAExB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAa9D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAYxD;;;OAGG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;YA6B5F,OAAO;CAgCtB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const DEFAULT_BASE = "https://api.github.com";
|
|
2
|
+
export class GitHubAdapter {
|
|
3
|
+
base;
|
|
4
|
+
token;
|
|
5
|
+
concurrency;
|
|
6
|
+
timeoutMs;
|
|
7
|
+
constructor(opts) {
|
|
8
|
+
this.base = (opts.baseUrl ?? DEFAULT_BASE).replace(/\/$/, "");
|
|
9
|
+
this.token = opts.token ?? process.env["GITHUB_TOKEN"];
|
|
10
|
+
this.concurrency = Math.max(1, opts.concurrency ?? 4);
|
|
11
|
+
this.timeoutMs = opts.timeoutMs ?? 15_000;
|
|
12
|
+
if (!opts.owner || !opts.repo) {
|
|
13
|
+
throw new Error("GitHubAdapter requires { owner, repo }");
|
|
14
|
+
}
|
|
15
|
+
this.owner = opts.owner;
|
|
16
|
+
this.repo = opts.repo;
|
|
17
|
+
}
|
|
18
|
+
owner;
|
|
19
|
+
repo;
|
|
20
|
+
async fetchPullRequest(num) {
|
|
21
|
+
const json = await this.request(`/repos/${this.owner}/${this.repo}/pulls/${num}`);
|
|
22
|
+
if (!json || json.message === "Not Found")
|
|
23
|
+
return null;
|
|
24
|
+
return {
|
|
25
|
+
number: json.number,
|
|
26
|
+
title: json.title ?? "",
|
|
27
|
+
body: json.body ?? "",
|
|
28
|
+
url: json.html_url ?? "",
|
|
29
|
+
state: json.state ?? "",
|
|
30
|
+
mergedAt: json.merged_at ?? undefined,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
async fetchIssue(num) {
|
|
34
|
+
const json = await this.request(`/repos/${this.owner}/${this.repo}/issues/${num}`);
|
|
35
|
+
if (!json || json.message === "Not Found")
|
|
36
|
+
return null;
|
|
37
|
+
return {
|
|
38
|
+
number: json.number,
|
|
39
|
+
title: json.title ?? "",
|
|
40
|
+
body: json.body ?? "",
|
|
41
|
+
url: json.html_url ?? "",
|
|
42
|
+
state: json.state ?? "",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Hydrate an array of commits with PR title/body in place.
|
|
47
|
+
* Skips commits that already have prBody, or have no prNumber.
|
|
48
|
+
*/
|
|
49
|
+
async hydrateCommits(commits, onProgress) {
|
|
50
|
+
const targets = commits.filter((c) => c.prNumber && !c.prBody);
|
|
51
|
+
let done = 0;
|
|
52
|
+
const queue = [...targets];
|
|
53
|
+
const workers = [];
|
|
54
|
+
for (let w = 0; w < this.concurrency; w++) {
|
|
55
|
+
workers.push((async () => {
|
|
56
|
+
while (queue.length) {
|
|
57
|
+
const c = queue.shift();
|
|
58
|
+
if (!c)
|
|
59
|
+
break;
|
|
60
|
+
try {
|
|
61
|
+
const pr = await this.fetchPullRequest(c.prNumber);
|
|
62
|
+
if (pr) {
|
|
63
|
+
c.prTitle = pr.title;
|
|
64
|
+
c.prBody = pr.body;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// best-effort hydration; skip failures, keep going
|
|
69
|
+
}
|
|
70
|
+
done++;
|
|
71
|
+
onProgress?.(done, targets.length);
|
|
72
|
+
}
|
|
73
|
+
})());
|
|
74
|
+
}
|
|
75
|
+
await Promise.all(workers);
|
|
76
|
+
}
|
|
77
|
+
async request(path, attempt = 0) {
|
|
78
|
+
const url = `${this.base}${path}`;
|
|
79
|
+
const headers = {
|
|
80
|
+
accept: "application/vnd.github+json",
|
|
81
|
+
"x-github-api-version": "2022-11-28",
|
|
82
|
+
"user-agent": "mneme/0.1",
|
|
83
|
+
};
|
|
84
|
+
if (this.token)
|
|
85
|
+
headers.authorization = `Bearer ${this.token}`;
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
88
|
+
try {
|
|
89
|
+
const res = await fetch(url, { headers, signal: controller.signal });
|
|
90
|
+
if (res.status === 404)
|
|
91
|
+
return null;
|
|
92
|
+
if (res.status === 429 || res.status >= 500) {
|
|
93
|
+
if (attempt >= 4) {
|
|
94
|
+
throw new Error(`GitHub API ${res.status} on ${path} after ${attempt} retries`);
|
|
95
|
+
}
|
|
96
|
+
const retryAfter = Number(res.headers.get("retry-after") ?? 0);
|
|
97
|
+
const backoffMs = retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 500;
|
|
98
|
+
await sleep(backoffMs);
|
|
99
|
+
return this.request(path, attempt + 1);
|
|
100
|
+
}
|
|
101
|
+
if (!res.ok) {
|
|
102
|
+
const body = await res.text();
|
|
103
|
+
throw new Error(`GitHub API ${res.status}: ${body.slice(0, 200)}`);
|
|
104
|
+
}
|
|
105
|
+
return (await res.json());
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function sleep(ms) {
|
|
113
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/git/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.test.d.ts","sourceRoot":"","sources":["../../src/git/github.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
2
|
+
import { GitHubAdapter } from "./github.js";
|
|
3
|
+
describe("GitHubAdapter", () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.spyOn(globalThis, "fetch");
|
|
6
|
+
});
|
|
7
|
+
afterEach(() => {
|
|
8
|
+
vi.restoreAllMocks();
|
|
9
|
+
});
|
|
10
|
+
it("requires owner + repo", () => {
|
|
11
|
+
expect(() => new GitHubAdapter({ owner: "", repo: "x" })).toThrow();
|
|
12
|
+
expect(() => new GitHubAdapter({ owner: "x", repo: "" })).toThrow();
|
|
13
|
+
});
|
|
14
|
+
it("fetchPullRequest returns parsed PR info", async () => {
|
|
15
|
+
globalThis.fetch.mockResolvedValue(new Response(JSON.stringify({
|
|
16
|
+
number: 42,
|
|
17
|
+
title: "Fix BigInt",
|
|
18
|
+
body: "Stripe sometimes sends bigint",
|
|
19
|
+
html_url: "https://github.com/foo/bar/pull/42",
|
|
20
|
+
state: "merged",
|
|
21
|
+
merged_at: "2025-01-15T10:00:00Z",
|
|
22
|
+
}), { status: 200, headers: { "content-type": "application/json" } }));
|
|
23
|
+
const a = new GitHubAdapter({ owner: "foo", repo: "bar", token: "tok" });
|
|
24
|
+
const pr = await a.fetchPullRequest(42);
|
|
25
|
+
expect(pr).not.toBeNull();
|
|
26
|
+
expect(pr.number).toBe(42);
|
|
27
|
+
expect(pr.title).toBe("Fix BigInt");
|
|
28
|
+
expect(pr.mergedAt).toBe("2025-01-15T10:00:00Z");
|
|
29
|
+
});
|
|
30
|
+
it("returns null on 404", async () => {
|
|
31
|
+
globalThis.fetch.mockResolvedValue(new Response("", { status: 404 }));
|
|
32
|
+
const a = new GitHubAdapter({ owner: "foo", repo: "bar" });
|
|
33
|
+
expect(await a.fetchPullRequest(999)).toBeNull();
|
|
34
|
+
});
|
|
35
|
+
it("hydrateCommits fills prTitle/prBody for commits with prNumber", async () => {
|
|
36
|
+
globalThis.fetch.mockResolvedValue(new Response(JSON.stringify({ number: 42, title: "T", body: "B", html_url: "", state: "merged" }), { status: 200, headers: { "content-type": "application/json" } }));
|
|
37
|
+
const a = new GitHubAdapter({ owner: "foo", repo: "bar" });
|
|
38
|
+
const commits = [
|
|
39
|
+
{
|
|
40
|
+
hash: "h",
|
|
41
|
+
shortHash: "h",
|
|
42
|
+
authorName: "x",
|
|
43
|
+
authorEmail: "x@x",
|
|
44
|
+
authorDate: "2025-01-01T00:00:00Z",
|
|
45
|
+
committerDate: "2025-01-01T00:00:00Z",
|
|
46
|
+
subject: "fix (#42)",
|
|
47
|
+
body: "",
|
|
48
|
+
parents: [],
|
|
49
|
+
files: [],
|
|
50
|
+
prNumber: 42,
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
await a.hydrateCommits(commits);
|
|
54
|
+
expect(commits[0].prTitle).toBe("T");
|
|
55
|
+
expect(commits[0].prBody).toBe("B");
|
|
56
|
+
});
|
|
57
|
+
it("does not refetch commits that already have a PR body", async () => {
|
|
58
|
+
const a = new GitHubAdapter({ owner: "foo", repo: "bar" });
|
|
59
|
+
const commits = [
|
|
60
|
+
{
|
|
61
|
+
hash: "h",
|
|
62
|
+
shortHash: "h",
|
|
63
|
+
authorName: "x",
|
|
64
|
+
authorEmail: "x@x",
|
|
65
|
+
authorDate: "2025-01-01T00:00:00Z",
|
|
66
|
+
committerDate: "2025-01-01T00:00:00Z",
|
|
67
|
+
subject: "S",
|
|
68
|
+
body: "",
|
|
69
|
+
parents: [],
|
|
70
|
+
files: [],
|
|
71
|
+
prNumber: 1,
|
|
72
|
+
prBody: "already there",
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
await a.hydrateCommits(commits);
|
|
76
|
+
expect(globalThis.fetch).not.toHaveBeenCalled();
|
|
77
|
+
});
|
|
78
|
+
it("sends Authorization header when token present", async () => {
|
|
79
|
+
globalThis.fetch.mockResolvedValue(new Response("{}", { status: 200, headers: { "content-type": "application/json" } }));
|
|
80
|
+
const a = new GitHubAdapter({ owner: "foo", repo: "bar", token: "secret" });
|
|
81
|
+
await a.fetchPullRequest(1);
|
|
82
|
+
const init = globalThis.fetch.mock.calls[0][1];
|
|
83
|
+
expect(init.headers.authorization).toBe("Bearer secret");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=github.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.test.js","sourceRoot":"","sources":["../../src/git/github.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitLab MR + issue body fetcher.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors GitHubAdapter but speaks the GitLab REST v4 API. Same job: hydrate
|
|
5
|
+
* commits with rich PR/issue text so retrieval has real "why" to chew on.
|
|
6
|
+
*
|
|
7
|
+
* Auth: read GITLAB_TOKEN from env, or accept it via opts.token. The token
|
|
8
|
+
* never touches disk — process memory only.
|
|
9
|
+
*
|
|
10
|
+
* Rate limiting: GitLab.com allows 2,000 req/min authenticated. Self-hosted
|
|
11
|
+
* varies. We respect Retry-After + exponential backoff on 429/5xx.
|
|
12
|
+
*
|
|
13
|
+
* Notes:
|
|
14
|
+
* - GitLab calls them "Merge Requests" (MR), not "Pull Requests".
|
|
15
|
+
* - Project ID can be a numeric id OR a URL-encoded path "owner%2Frepo".
|
|
16
|
+
* - GitLab subgroups are common (e.g. `group/sub/repo`) — we URL-encode the
|
|
17
|
+
* full path so they work without configuration.
|
|
18
|
+
*/
|
|
19
|
+
import type { Commit } from "../types.js";
|
|
20
|
+
export interface GitLabFetchOptions {
|
|
21
|
+
/** owner/repo or owner/group/repo (will be URL-encoded). */
|
|
22
|
+
projectPath: string;
|
|
23
|
+
/** Personal access token (api scope). Read from GITLAB_TOKEN if absent. */
|
|
24
|
+
token?: string;
|
|
25
|
+
/** Override base URL for self-hosted. Default https://gitlab.com. */
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
/** Concurrent request cap. Default 4. */
|
|
28
|
+
concurrency?: number;
|
|
29
|
+
/** Per-request timeout (ms). Default 15_000. */
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
onProgress?: (done: number, total: number) => void;
|
|
32
|
+
}
|
|
33
|
+
export interface MergeRequestInfo {
|
|
34
|
+
iid: number;
|
|
35
|
+
title: string;
|
|
36
|
+
description: string;
|
|
37
|
+
url: string;
|
|
38
|
+
state: string;
|
|
39
|
+
mergedAt?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface GitLabIssueInfo {
|
|
42
|
+
iid: number;
|
|
43
|
+
title: string;
|
|
44
|
+
description: string;
|
|
45
|
+
url: string;
|
|
46
|
+
state: string;
|
|
47
|
+
}
|
|
48
|
+
export declare class GitLabAdapter {
|
|
49
|
+
readonly source: "gitlab";
|
|
50
|
+
private readonly base;
|
|
51
|
+
private readonly token?;
|
|
52
|
+
private readonly concurrency;
|
|
53
|
+
private readonly timeoutMs;
|
|
54
|
+
private readonly projectPath;
|
|
55
|
+
private readonly encodedPath;
|
|
56
|
+
constructor(opts: GitLabFetchOptions);
|
|
57
|
+
fetchMergeRequest(iid: number): Promise<MergeRequestInfo | null>;
|
|
58
|
+
fetchIssue(iid: number): Promise<GitLabIssueInfo | null>;
|
|
59
|
+
/**
|
|
60
|
+
* Hydrate commits in place. Skips commits without `prNumber` or with `prBody`.
|
|
61
|
+
* In GitLab terminology `prNumber` actually means MR `iid`.
|
|
62
|
+
*/
|
|
63
|
+
hydrateCommits(commits: Commit[], onProgress?: (done: number, total: number) => void): Promise<void>;
|
|
64
|
+
private request;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=gitlab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../src/git/gitlab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpD;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAID,qBAAa,aAAa;IACxB,QAAQ,CAAC,MAAM,EAAG,QAAQ,CAAU;IAEpC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,IAAI,EAAE,kBAAkB;IAY9B,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAehE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAc9D;;;OAGG;IACG,cAAc,CAClB,OAAO,EAAE,MAAM,EAAE,EACjB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACjD,OAAO,CAAC,IAAI,CAAC;YA6BF,OAAO;CA+BtB"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const DEFAULT_BASE = "https://gitlab.com";
|
|
2
|
+
export class GitLabAdapter {
|
|
3
|
+
source = "gitlab";
|
|
4
|
+
base;
|
|
5
|
+
token;
|
|
6
|
+
concurrency;
|
|
7
|
+
timeoutMs;
|
|
8
|
+
projectPath;
|
|
9
|
+
encodedPath;
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
if (!opts.projectPath) {
|
|
12
|
+
throw new Error("GitLabAdapter requires { projectPath } (e.g. 'group/repo')");
|
|
13
|
+
}
|
|
14
|
+
this.base = (opts.baseUrl ?? DEFAULT_BASE).replace(/\/$/, "");
|
|
15
|
+
this.token = opts.token ?? process.env["GITLAB_TOKEN"];
|
|
16
|
+
this.concurrency = Math.max(1, opts.concurrency ?? 4);
|
|
17
|
+
this.timeoutMs = opts.timeoutMs ?? 15_000;
|
|
18
|
+
this.projectPath = opts.projectPath.replace(/^\//, "").replace(/\.git$/, "");
|
|
19
|
+
this.encodedPath = encodeURIComponent(this.projectPath);
|
|
20
|
+
}
|
|
21
|
+
async fetchMergeRequest(iid) {
|
|
22
|
+
const json = await this.request(`/api/v4/projects/${this.encodedPath}/merge_requests/${iid}`);
|
|
23
|
+
if (!json || isNotFound(json))
|
|
24
|
+
return null;
|
|
25
|
+
return {
|
|
26
|
+
iid: Number(json.iid),
|
|
27
|
+
title: String(json.title ?? ""),
|
|
28
|
+
description: String(json.description ?? ""),
|
|
29
|
+
url: String(json.web_url ?? ""),
|
|
30
|
+
state: String(json.state ?? ""),
|
|
31
|
+
mergedAt: json.merged_at ?? undefined,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
async fetchIssue(iid) {
|
|
35
|
+
const json = await this.request(`/api/v4/projects/${this.encodedPath}/issues/${iid}`);
|
|
36
|
+
if (!json || isNotFound(json))
|
|
37
|
+
return null;
|
|
38
|
+
return {
|
|
39
|
+
iid: Number(json.iid),
|
|
40
|
+
title: String(json.title ?? ""),
|
|
41
|
+
description: String(json.description ?? ""),
|
|
42
|
+
url: String(json.web_url ?? ""),
|
|
43
|
+
state: String(json.state ?? ""),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Hydrate commits in place. Skips commits without `prNumber` or with `prBody`.
|
|
48
|
+
* In GitLab terminology `prNumber` actually means MR `iid`.
|
|
49
|
+
*/
|
|
50
|
+
async hydrateCommits(commits, onProgress) {
|
|
51
|
+
const targets = commits.filter((c) => c.prNumber && !c.prBody);
|
|
52
|
+
let done = 0;
|
|
53
|
+
const queue = [...targets];
|
|
54
|
+
const workers = [];
|
|
55
|
+
for (let w = 0; w < this.concurrency; w++) {
|
|
56
|
+
workers.push((async () => {
|
|
57
|
+
while (queue.length) {
|
|
58
|
+
const c = queue.shift();
|
|
59
|
+
if (!c)
|
|
60
|
+
break;
|
|
61
|
+
try {
|
|
62
|
+
const mr = await this.fetchMergeRequest(c.prNumber);
|
|
63
|
+
if (mr) {
|
|
64
|
+
c.prTitle = mr.title;
|
|
65
|
+
c.prBody = mr.description;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// best-effort hydration
|
|
70
|
+
}
|
|
71
|
+
done++;
|
|
72
|
+
onProgress?.(done, targets.length);
|
|
73
|
+
}
|
|
74
|
+
})());
|
|
75
|
+
}
|
|
76
|
+
await Promise.all(workers);
|
|
77
|
+
}
|
|
78
|
+
async request(path, attempt = 0) {
|
|
79
|
+
const url = `${this.base}${path}`;
|
|
80
|
+
const headers = {
|
|
81
|
+
accept: "application/json",
|
|
82
|
+
"user-agent": "mneme/0.1",
|
|
83
|
+
};
|
|
84
|
+
if (this.token)
|
|
85
|
+
headers["private-token"] = this.token;
|
|
86
|
+
const controller = new AbortController();
|
|
87
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
88
|
+
try {
|
|
89
|
+
const res = await fetch(url, { headers, signal: controller.signal });
|
|
90
|
+
if (res.status === 404)
|
|
91
|
+
return null;
|
|
92
|
+
if (res.status === 429 || res.status >= 500) {
|
|
93
|
+
if (attempt >= 4) {
|
|
94
|
+
throw new Error(`GitLab API ${res.status} on ${path} after ${attempt} retries`);
|
|
95
|
+
}
|
|
96
|
+
const retryAfter = Number(res.headers.get("retry-after") ?? 0);
|
|
97
|
+
const backoffMs = retryAfter > 0 ? retryAfter * 1000 : 2 ** attempt * 500;
|
|
98
|
+
await sleep(backoffMs);
|
|
99
|
+
return this.request(path, attempt + 1);
|
|
100
|
+
}
|
|
101
|
+
if (!res.ok) {
|
|
102
|
+
const body = await res.text();
|
|
103
|
+
throw new Error(`GitLab API ${res.status}: ${body.slice(0, 200)}`);
|
|
104
|
+
}
|
|
105
|
+
return (await res.json());
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function isNotFound(json) {
|
|
113
|
+
if (!json || typeof json !== "object")
|
|
114
|
+
return false;
|
|
115
|
+
const msg = String(json.message ?? "").toLowerCase();
|
|
116
|
+
return msg.includes("not found") || msg === "404";
|
|
117
|
+
}
|
|
118
|
+
function sleep(ms) {
|
|
119
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=gitlab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.js","sourceRoot":"","sources":["../../src/git/gitlab.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.test.d.ts","sourceRoot":"","sources":["../../src/git/gitlab.test.ts"],"names":[],"mappings":""}
|