@kud/gh-ink 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +12 -2
- package/dist/index.js +64 -28
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -270,6 +270,7 @@ declare const runHere: (cmd: string) => void;
|
|
|
270
270
|
declare const COLS: number;
|
|
271
271
|
declare const topLevelCount: (s: Section) => number;
|
|
272
272
|
declare const drillCmd: (item: AnyItem) => string | null;
|
|
273
|
+
declare const explainGhAction: (e: unknown) => string;
|
|
273
274
|
declare const buildActions: (item: AnyItem, login: string, showFlash: (msg: string) => void, jiraBase?: string, jiraKeyRe?: RegExp, jiraTransitions?: JiraTransition[], onRefresh?: () => void, onRemove?: (item: GHItem) => void, onOpenView?: (item: AnyItem) => boolean, ext?: {
|
|
274
275
|
extensions?: InboxExtension[];
|
|
275
276
|
onOpenExt?: (id: string, target: ExtensionTarget) => void;
|
|
@@ -314,7 +315,7 @@ declare const ActionMenu: ({ item, actions, cursor, }: {
|
|
|
314
315
|
cursor: number;
|
|
315
316
|
}) => React.JSX.Element;
|
|
316
317
|
declare const signatureOf: (sections: Section[]) => string;
|
|
317
|
-
declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jiraKeyRe, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, watchPath, watchDebounceMs, extensions, tabHelp, emptyHint, }: {
|
|
318
|
+
declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jiraKeyRe, jiraTransitions, hasCiStatus, ciJob, ciFetcher, ciPollMs, watchPath, watchDebounceMs, watchJitterMs, extensions, tabHelp, emptyHint, }: {
|
|
318
319
|
fetcher: () => Promise<{
|
|
319
320
|
sections: Section[];
|
|
320
321
|
login: string;
|
|
@@ -342,6 +343,15 @@ declare const App: ({ fetcher, cacheKey, title, detailFor, origin, jiraBase, jir
|
|
|
342
343
|
watchPath?: string;
|
|
343
344
|
/** Bursts of writes to collapse into one refetch. */
|
|
344
345
|
watchDebounceMs?: number;
|
|
346
|
+
/**
|
|
347
|
+
* Random spread added to `watchDebounceMs`, so several cockpits woken by one
|
|
348
|
+
* signal take turns instead of stampeding. The first to wake fetches and
|
|
349
|
+
* writes the shared cache; the others adopt it and pay nothing.
|
|
350
|
+
*
|
|
351
|
+
* Defaults to 0 — no randomness unless a host asks for it. Set it to comfortably
|
|
352
|
+
* more than one round trip when you expect several instances to be open.
|
|
353
|
+
*/
|
|
354
|
+
watchJitterMs?: number;
|
|
345
355
|
extensions?: InboxExtension[];
|
|
346
356
|
}) => React.JSX.Element;
|
|
347
357
|
|
|
@@ -443,4 +453,4 @@ declare const matchesFilter: (repo: string, filter: RepoFilter) => boolean;
|
|
|
443
453
|
*/
|
|
444
454
|
declare const parsePatterns: (value: string) => string[];
|
|
445
455
|
|
|
446
|
-
export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxConfig, type InboxExtension, type JiraTransition, type OriginSplit, type RepoFilter, type RepoHeader, type RepoProfile, type Section, type ShowLess, type ShowMore, type Standing, type SubgroupHeader, type TaskRow, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
|
456
|
+
export { type Action, ActionMenu, type AnyItem, App, COLS, type CiStatus, CiStatusLine, type CiStatusState, CommentsPanel, type CommentsPanelProps, type DetailContext, type ExplainSection, type ExtensionTarget, type GHDetail, type GHItem, HealthPanel, type HealthPanelProps, type InboxConfig, type InboxExtension, type JiraTransition, type OriginSplit, type RepoFilter, type RepoHeader, type RepoProfile, type Section, type ShowLess, type ShowMore, type Standing, type SubgroupHeader, type TaskRow, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
package/dist/index.js
CHANGED
|
@@ -1317,9 +1317,22 @@ var UNSUBSCRIBE_MUTATION = `
|
|
|
1317
1317
|
}
|
|
1318
1318
|
}
|
|
1319
1319
|
`;
|
|
1320
|
+
var GH_ACTION_FAILURES = [
|
|
1321
|
+
[/rate limit|RATE_LIMIT/i, "GitHub rate limit spent \u2014 it resets within the hour"],
|
|
1322
|
+
[/\b401\b|not logged in|authentication/i, "gh is not authenticated"],
|
|
1323
|
+
[/\b403\b|must have admin|not authorized|permission/i, "not permitted on this repo"],
|
|
1324
|
+
[/\b50[0234]\b/, "GitHub's API is failing (5xx)"],
|
|
1325
|
+
[/ENOTFOUND|EAI_AGAIN|ETIMEDOUT|ECONNREFUSED/i, "no route to GitHub"]
|
|
1326
|
+
];
|
|
1327
|
+
var explainGhAction = (e) => {
|
|
1328
|
+
const err = e;
|
|
1329
|
+
const raw = (err.stderr ?? "").trim().split("\n").filter(Boolean).pop() ?? err.message ?? "no reason reported";
|
|
1330
|
+
const human = GH_ACTION_FAILURES.find(([re]) => re.test(raw))?.[1];
|
|
1331
|
+
return human ? `${human} (${raw})` : raw;
|
|
1332
|
+
};
|
|
1320
1333
|
var unsubscribeFrom = async (item) => {
|
|
1321
|
-
const
|
|
1322
|
-
const found = await quietly`gh
|
|
1334
|
+
const route = item.kind === "pr" ? "pulls" : "issues";
|
|
1335
|
+
const found = await quietly`gh api repos/${item.repo}/${route}/${item.number} --jq .node_id`;
|
|
1323
1336
|
const id = found.stdout.trim();
|
|
1324
1337
|
if (!id) throw new Error(`no node id for ${item.repo}#${item.number}`);
|
|
1325
1338
|
await quietly`gh api graphql -f query=${UNSUBSCRIBE_MUTATION} -f id=${id}`;
|
|
@@ -1419,8 +1432,8 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1419
1432
|
void quietly`gh pr edit ${item.number} --repo ${item.repo} --remove-reviewer ${login}`.then(() => {
|
|
1420
1433
|
showFlash(`\u2713 Removed you as reviewer on #${item.number}`);
|
|
1421
1434
|
ext?.onActed?.();
|
|
1422
|
-
}).catch(() => {
|
|
1423
|
-
showFlash(`\u2717
|
|
1435
|
+
}).catch((e) => {
|
|
1436
|
+
showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`);
|
|
1424
1437
|
onRefresh?.();
|
|
1425
1438
|
});
|
|
1426
1439
|
}
|
|
@@ -1431,7 +1444,9 @@ var buildActions = (item, login, showFlash, jiraBase, jiraKeyRe, jiraTransitions
|
|
|
1431
1444
|
hint: "u",
|
|
1432
1445
|
run: () => {
|
|
1433
1446
|
showFlash(`\u22EF Unsubscribing from #${item.number}\u2026`);
|
|
1434
|
-
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch(
|
|
1447
|
+
void unsubscribeFrom(item).then(() => showFlash(`\u2713 Unsubscribed from #${item.number}`)).catch(
|
|
1448
|
+
(e) => showFlash(`\u2717 #${item.number}: ${explainGhAction(e)}`)
|
|
1449
|
+
);
|
|
1435
1450
|
}
|
|
1436
1451
|
});
|
|
1437
1452
|
if (item.kind === "pr" && item.branch) {
|
|
@@ -2466,7 +2481,9 @@ var BrowseScreen = ({
|
|
|
2466
2481
|
}
|
|
2467
2482
|
if (input === "u" && (activeItem.kind === "pr" || activeItem.kind === "issue")) {
|
|
2468
2483
|
showFlash(`\u22EF Unsubscribing from #${activeItem.number}\u2026`);
|
|
2469
|
-
void unsubscribeFrom(activeItem).then(() => showFlash(`\u2713 Unsubscribed from #${activeItem.number}`)).catch(
|
|
2484
|
+
void unsubscribeFrom(activeItem).then(() => showFlash(`\u2713 Unsubscribed from #${activeItem.number}`)).catch(
|
|
2485
|
+
(e) => showFlash(`\u2717 #${activeItem.number}: ${explainGhAction(e)}`)
|
|
2486
|
+
);
|
|
2470
2487
|
return;
|
|
2471
2488
|
}
|
|
2472
2489
|
if (input === "x" && activeItem.kind === "pr" && activeItem.standing === "queued" && login) {
|
|
@@ -2475,8 +2492,8 @@ var BrowseScreen = ({
|
|
|
2475
2492
|
void quietly`gh pr edit ${activeItem.number} --repo ${activeItem.repo} --remove-reviewer ${login}`.then(() => {
|
|
2476
2493
|
showFlash(`\u2713 Removed you as reviewer on #${activeItem.number}`);
|
|
2477
2494
|
onActed?.();
|
|
2478
|
-
}).catch(() => {
|
|
2479
|
-
showFlash(`\u2717
|
|
2495
|
+
}).catch((e) => {
|
|
2496
|
+
showFlash(`\u2717 #${activeItem.number}: ${explainGhAction(e)}`);
|
|
2480
2497
|
onRefresh?.();
|
|
2481
2498
|
});
|
|
2482
2499
|
return;
|
|
@@ -2705,6 +2722,11 @@ var App = ({
|
|
|
2705
2722
|
ciPollMs = 6e4,
|
|
2706
2723
|
watchPath,
|
|
2707
2724
|
watchDebounceMs = 400,
|
|
2725
|
+
// Zero by default, deliberately. A package that adds randomness to its own
|
|
2726
|
+
// timing makes every host's tests flaky — this one's went green locally on a
|
|
2727
|
+
// lucky draw and red in CI. It is also a host concern: only somebody running
|
|
2728
|
+
// several cockpits at once needs the stagger, and only they know how many.
|
|
2729
|
+
watchJitterMs = 0,
|
|
2708
2730
|
extensions,
|
|
2709
2731
|
tabHelp,
|
|
2710
2732
|
emptyHint
|
|
@@ -2802,24 +2824,7 @@ var App = ({
|
|
|
2802
2824
|
setFetchedAt(Date.now());
|
|
2803
2825
|
setRefreshError(null);
|
|
2804
2826
|
if (hasCiStatus) applyCiStatus(fresh.ciStatus ?? null);
|
|
2805
|
-
|
|
2806
|
-
if (!displayedKey.current) {
|
|
2807
|
-
if (fresh.sections.length === 0) {
|
|
2808
|
-
setState({ phase: "empty" });
|
|
2809
|
-
return;
|
|
2810
|
-
}
|
|
2811
|
-
showData(fresh.sections, fresh.login);
|
|
2812
|
-
} else if (freshKey !== displayedKey.current) {
|
|
2813
|
-
const { counts } = diffSections(
|
|
2814
|
-
displayedSections.current,
|
|
2815
|
-
fresh.sections
|
|
2816
|
-
);
|
|
2817
|
-
if (counts.added + counts.removed + counts.changed === 0)
|
|
2818
|
-
showData(fresh.sections, fresh.login);
|
|
2819
|
-
else setPending(fresh);
|
|
2820
|
-
} else {
|
|
2821
|
-
setPending(null);
|
|
2822
|
-
}
|
|
2827
|
+
receive(fresh);
|
|
2823
2828
|
}).catch((err) => {
|
|
2824
2829
|
setRefreshing(false);
|
|
2825
2830
|
const message = err.message;
|
|
@@ -2830,6 +2835,26 @@ var App = ({
|
|
|
2830
2835
|
setRefreshError({ message, at: Date.now() });
|
|
2831
2836
|
});
|
|
2832
2837
|
};
|
|
2838
|
+
const receive = (fresh) => {
|
|
2839
|
+
const freshKey = signatureOf(fresh.sections);
|
|
2840
|
+
if (!displayedKey.current) {
|
|
2841
|
+
if (fresh.sections.length === 0) {
|
|
2842
|
+
setState({ phase: "empty" });
|
|
2843
|
+
return;
|
|
2844
|
+
}
|
|
2845
|
+
showData(fresh.sections, fresh.login);
|
|
2846
|
+
} else if (freshKey !== displayedKey.current) {
|
|
2847
|
+
const { counts } = diffSections(
|
|
2848
|
+
displayedSections.current,
|
|
2849
|
+
fresh.sections
|
|
2850
|
+
);
|
|
2851
|
+
if (counts.added + counts.removed + counts.changed === 0)
|
|
2852
|
+
showData(fresh.sections, fresh.login);
|
|
2853
|
+
else setPending(fresh);
|
|
2854
|
+
} else {
|
|
2855
|
+
setPending(null);
|
|
2856
|
+
}
|
|
2857
|
+
};
|
|
2833
2858
|
const applyOrRefresh = () => {
|
|
2834
2859
|
if (pending) showData(pending.sections, pending.login);
|
|
2835
2860
|
else revalidate(true);
|
|
@@ -2882,7 +2907,18 @@ var App = ({
|
|
|
2882
2907
|
watcher = watch(dir, (_event, changed) => {
|
|
2883
2908
|
if (!live || changed && changed !== name) return;
|
|
2884
2909
|
if (timer) clearTimeout(timer);
|
|
2885
|
-
|
|
2910
|
+
const firedAt = Date.now();
|
|
2911
|
+
const delay = watchDebounceMs + Math.floor(Math.random() * watchJitterMs);
|
|
2912
|
+
timer = setTimeout(() => {
|
|
2913
|
+
if (!live) return;
|
|
2914
|
+
const shared = cacheKey ? readCache(cacheKey) : null;
|
|
2915
|
+
if (shared && shared.at > firedAt) {
|
|
2916
|
+
receive({ sections: shared.sections, login: shared.login });
|
|
2917
|
+
setFetchedAt(shared.at);
|
|
2918
|
+
return;
|
|
2919
|
+
}
|
|
2920
|
+
revalidate();
|
|
2921
|
+
}, delay);
|
|
2886
2922
|
});
|
|
2887
2923
|
} catch {
|
|
2888
2924
|
return;
|
|
@@ -3029,4 +3065,4 @@ var matchesFilter = (repo, filter) => {
|
|
|
3029
3065
|
};
|
|
3030
3066
|
var parsePatterns = (value) => value.split(",").map((p) => p.trim()).filter(Boolean);
|
|
3031
3067
|
|
|
3032
|
-
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
|
3068
|
+
export { ActionMenu, App, COLS, CiStatusLine, CommentsPanel, HealthPanel, buildActions, buildCheckoutCmd, checkoutDirs, clipboard, configureInbox, drillCmd, explainGhAction, explainItem, filterByOrigin, filterByRepos, filterBySearch, fitCount, gapsAbove, healthColor, healthDisplay, healthGlyph, healthLegend, inboxConfig, insertRepoHeaders, itermRun, jumpToRepo, jumpToRepoPane, layoutGHItems, matchesFilter, maxViewStart, moveCursor, openInTab, parsePatterns, profileOf, readCache, relativeTime, renderMarkdown, repoPriority, reposInSections, resetInboxConfig, resolveRepoPath, runHere, runInPane, runInPaneHorizontal, sameCiStatusState, signatureOf, sortByRecency, sortItems, toCiStatusState, topLevelCount, truncate, useActionMenu, whoseMove, windowCount, withHeaders, withoutItem, writeCache };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kud/gh-ink",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Ink components for rendering GitHub PR review comments and health — controlled, presentation-only, built on @kud/ink-ui and fed by @kud/gh.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|