@archme/cli 0.11.2 → 0.11.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.
Files changed (2) hide show
  1. package/dist/cli.js +107 -2
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -17291,6 +17291,16 @@ var init_defaultBranch = __esm({
17291
17291
  });
17292
17292
 
17293
17293
  // ../shared/prRefs.ts
17294
+ function parsePrRefs(message) {
17295
+ if (!message) return [];
17296
+ const refs = [...message.matchAll(PR_REF)].map((m) => Number(m[1]));
17297
+ const bare = refs.length ? null : bareMergePrNumber(message);
17298
+ return bare === null ? refs : [bare];
17299
+ }
17300
+ function lastPrRef(message) {
17301
+ const refs = parsePrRefs(message);
17302
+ return refs[refs.length - 1] ?? null;
17303
+ }
17294
17304
  function pushCommitSubject(message) {
17295
17305
  if (message == null) return null;
17296
17306
  const [subject = "", ...body] = message.split("\n");
@@ -17298,10 +17308,15 @@ function pushCommitSubject(message) {
17298
17308
  const title = body.find((line) => line.trim())?.trim();
17299
17309
  return pr && title && !TRAILER_LINE.test(title) ? `${title} (#${pr[1]})` : subject;
17300
17310
  }
17301
- var MERGE_PR_SUBJECT, TRAILER_LINE;
17311
+ function bareMergePrNumber(subject) {
17312
+ const pr = subject ? MERGE_PR_SUBJECT.exec(subject.trim()) : null;
17313
+ return pr ? Number(pr[1]) : null;
17314
+ }
17315
+ var PR_REF, MERGE_PR_SUBJECT, TRAILER_LINE;
17302
17316
  var init_prRefs = __esm({
17303
17317
  "../shared/prRefs.ts"() {
17304
17318
  init_repoContainer();
17319
+ PR_REF = /\(#(\d+)\)/g;
17305
17320
  MERGE_PR_SUBJECT = /^Merge pull request #(\d+) from \S+$/;
17306
17321
  TRAILER_LINE = /^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)+:\s/;
17307
17322
  }
@@ -17676,6 +17691,87 @@ var init_localPrAdapters = __esm({
17676
17691
  }
17677
17692
  });
17678
17693
 
17694
+ // ../shared/pushSubject.ts
17695
+ async function resolvePushSubject(message, push, lookup, timeoutMs = PUSH_SUBJECT_TIMEOUT_MS) {
17696
+ const subject = pushCommitSubject(message);
17697
+ if (!subject) return subject;
17698
+ const bare = bareMergePrNumber(subject);
17699
+ if (bare === null && lastPrRef(subject) !== null) return subject;
17700
+ try {
17701
+ const found = await withTimeout(findPr(subject, bare, push, lookup), timeoutMs);
17702
+ return found ? `${found.title} (#${found.number})` : subject;
17703
+ } catch {
17704
+ return subject;
17705
+ }
17706
+ }
17707
+ async function findPr(subject, bare, push, lookup) {
17708
+ if (bare !== null) {
17709
+ const title = (await lookup.pullTitle(bare))?.trim();
17710
+ return title ? { number: bare, title } : null;
17711
+ }
17712
+ const pr = mergedPrForCommit(await lookup.pullsForCommit(push.sha), push);
17713
+ return pr ? { number: pr.number, title: pr.title?.trim() || subject } : null;
17714
+ }
17715
+ function mergedPrForCommit(prs, push) {
17716
+ const merged = prs.filter((p) => p.mergedAt && (!p.baseRef || p.baseRef === push.branch));
17717
+ const exact = merged.filter((p) => p.mergeCommitSha === push.sha);
17718
+ if (exact.length === 1) return exact[0] ?? null;
17719
+ return merged.length === 1 ? merged[0] ?? null : null;
17720
+ }
17721
+ function withTimeout(work, ms) {
17722
+ let timer;
17723
+ const expired = new Promise((_, reject) => {
17724
+ timer = setTimeout(() => reject(new Error("timed out")), ms);
17725
+ });
17726
+ return Promise.race([work, expired]).finally(() => clearTimeout(timer));
17727
+ }
17728
+ function githubPrLookup(opts) {
17729
+ const doFetch = opts.fetch ?? fetch;
17730
+ let tokens = null;
17731
+ async function get(path7) {
17732
+ tokens ??= opts.tokens();
17733
+ for (const token of await tokens) {
17734
+ const res = await doFetch(`${GITHUB_API}/repos/${opts.repo}${path7}`, {
17735
+ headers: {
17736
+ Accept: "application/vnd.github+json",
17737
+ "X-GitHub-Api-Version": "2022-11-28",
17738
+ "User-Agent": "archme",
17739
+ ...token ? { Authorization: `Bearer ${token}` } : {}
17740
+ }
17741
+ });
17742
+ if (REFUSED.has(res.status)) continue;
17743
+ return res.ok ? await res.json() : null;
17744
+ }
17745
+ return null;
17746
+ }
17747
+ return {
17748
+ async pullTitle(prNumber) {
17749
+ const pr = await get(`/pulls/${prNumber}`);
17750
+ return typeof pr?.title === "string" ? pr.title : null;
17751
+ },
17752
+ async pullsForCommit(sha) {
17753
+ const prs = await get(`/commits/${sha}/pulls`);
17754
+ if (!Array.isArray(prs)) return [];
17755
+ return prs.map((p) => ({
17756
+ number: Number(p.number),
17757
+ title: typeof p.title === "string" ? p.title : null,
17758
+ mergedAt: p.merged_at ?? null,
17759
+ mergeCommitSha: p.merge_commit_sha ?? null,
17760
+ baseRef: p.base?.ref ?? null
17761
+ }));
17762
+ }
17763
+ };
17764
+ }
17765
+ var PUSH_SUBJECT_TIMEOUT_MS, GITHUB_API, REFUSED;
17766
+ var init_pushSubject = __esm({
17767
+ "../shared/pushSubject.ts"() {
17768
+ init_prRefs();
17769
+ PUSH_SUBJECT_TIMEOUT_MS = 3e3;
17770
+ GITHUB_API = "https://api.github.com";
17771
+ REFUSED = /* @__PURE__ */ new Set([401, 403, 404]);
17772
+ }
17773
+ });
17774
+
17679
17775
  // src/daemon/localPoller.ts
17680
17776
  async function pollRepoOnce(entry, deps) {
17681
17777
  const repo = `${entry.repoOwner}/${entry.repoName}`;
@@ -17849,6 +17945,14 @@ function makePollerDeps(client, log, gateTimeoutMs) {
17849
17945
  },
17850
17946
  async enqueue(entry, row, beforeSha, afterSha) {
17851
17947
  const meta = await readCommitMeta(entry.mirrorPath, afterSha);
17948
+ const subject = await resolvePushSubject(
17949
+ meta.message || null,
17950
+ { sha: afterSha, branch: row.repoBranch },
17951
+ githubPrLookup({
17952
+ repo: `${entry.repoOwner}/${entry.repoName}`,
17953
+ tokens: async () => (await resolveGithubTokens()).map((t) => t.token)
17954
+ })
17955
+ );
17852
17956
  const { data, error } = await client.rpc("enqueue_local_archme_job", {
17853
17957
  p_watched_repo_id: entry.watchedRepoId,
17854
17958
  // From the ROW, not the registry: these decide which lane the job lands
@@ -17857,7 +17961,7 @@ function makePollerDeps(client, log, gateTimeoutMs) {
17857
17961
  p_archme_branch: row.archmeBranch,
17858
17962
  p_before_sha: beforeSha,
17859
17963
  p_after_sha: afterSha,
17860
- p_commit_message: meta.message || null,
17964
+ p_commit_message: subject,
17861
17965
  p_head_commit_timestamp: meta.committedAt,
17862
17966
  p_author_name: meta.authorName,
17863
17967
  // `author_login` is a GitHub concept with no git equivalent. The webhook
@@ -17884,6 +17988,7 @@ var init_localPoller = __esm({
17884
17988
  init_branchMarker();
17885
17989
  init_localGit();
17886
17990
  init_localPrAdapters();
17991
+ init_pushSubject();
17887
17992
  DEFAULT_POLL_INTERVAL_MS = 6e4;
17888
17993
  DEFAULT_GATE_TIMEOUT_MS = 4 * 60 * 60 * 1e3;
17889
17994
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@archme/cli",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
4
4
  "description": "Map your codebase into a living architecture diagram: run `archme init` in a repo to scan it, `archme daemon start` to keep it current on every commit.",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",