@kud/gh 0.4.1 → 0.5.1

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/inbox.d.ts CHANGED
@@ -1,9 +1,31 @@
1
+ /**
2
+ * How much of each row to ask for.
3
+ *
4
+ * `full` carries health, conversation and labels — everything `computeHealth`
5
+ * and a conversation line need. `minimal` carries identity only: type, number,
6
+ * title, url, draft, timestamp, repo.
7
+ *
8
+ * This is a cost axis, not a taste one. Measured against GitHub's own
9
+ * `rateLimit { cost }`: the full query costs **111 points** of a 5000/hour
10
+ * budget, at a nodeCount of ~25,550 — because `statusCheckRollup.contexts`
11
+ * appears in five PR fragments and `reviewThreads(first: 50)` multiplies
12
+ * beneath each one. A caller that renders a title and a link was paying for
13
+ * every check run on every open PR. At 111 a poller gets 45 refreshes an hour.
14
+ */
15
+ export type InboxShape = "full" | "minimal";
1
16
  /** A repo-scoped or account-wide inbox. */
2
17
  export type InboxQueryOptions = {
3
18
  /** `owner/name`. Scopes every search to one repository. */
4
19
  repo?: string;
5
20
  /** How far back `recentlyDone` looks. Defaults to 14 days. */
6
21
  doneWithinDays?: number;
22
+ /**
23
+ * Defaults to `full`, so every existing caller keeps what it had. Reach for
24
+ * `minimal` only when nothing downstream reads health, conversation or
25
+ * labels — those fields do not degrade, they vanish, and `computeHealth`
26
+ * falls through to a wrong answer rather than an absent one.
27
+ */
28
+ shape?: InboxShape;
7
29
  };
8
30
  /**
9
31
  * Build the inbox query. Without `repo`, it is account-wide.
@@ -14,5 +36,5 @@ export type InboxQueryOptions = {
14
36
  * render empty and read as "nothing to do here". So `repo:` REPLACES
15
37
  * `user:@me` rather than joining it.
16
38
  */
17
- export declare const buildInboxQuery: ({ repo, doneWithinDays, }?: InboxQueryOptions) => string;
39
+ export declare const buildInboxQuery: ({ repo, doneWithinDays, shape, }?: InboxQueryOptions) => string;
18
40
  //# sourceMappingURL=inbox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAQA,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAmDD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,4BAG7B,iBAAsB,WAqFxB,CAAA"}
1
+ {"version":3,"file":"inbox.d.ts","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,CAAA;AAE3C,2CAA2C;AAC3C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AA6DD;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,mCAI7B,iBAAsB,WAwGxB,CAAA"}
package/dist/inbox.js CHANGED
@@ -8,10 +8,20 @@
8
8
  // computeHealth's precedence needs reviewDecision, mergeable and the check
9
9
  // rollup. A fragment that omits them does NOT degrade gracefully — it falls
10
10
  // straight through to whatever token is left, so a PR you already sent back
11
- // renders as "awaiting review" and a failing one renders as quiet. Every PR
12
- // fragment therefore carries the whole set; none of it is optional.
11
+ // renders as "awaiting review" and a failing one renders as quiet.
12
+ //
13
+ // So this set is all-or-nothing, which is exactly why `shape` drops it whole
14
+ // rather than field by field: a caller either computes health and needs every
15
+ // one of these, or renders none of it and should pay for none of it. There is
16
+ // no coherent middle, and the `minimal` shape is not a smaller health — it is
17
+ // the absence of health.
18
+ //
19
+ // `isDraft` is deliberately NOT in here. It is identity, not health, and a
20
+ // minimal caller still has to tell a draft from an open PR, so it sits in the
21
+ // base field list beside `number` and `title` where dropping health cannot
22
+ // take it with them.
13
23
  const PR_HEALTH = `
14
- isDraft reviewDecision mergeable
24
+ reviewDecision mergeable
15
25
  statusCheckRollup { contexts(first: 20) { nodes {
16
26
  ... on CheckRun { name conclusion status }
17
27
  ... on StatusContext { context state }
@@ -57,38 +67,56 @@ const sinceDay = (days) => new Date(Date.now() - days * 86400 * 1000).toISOStrin
57
67
  * render empty and read as "nothing to do here". So `repo:` REPLACES
58
68
  * `user:@me` rather than joining it.
59
69
  */
60
- export const buildInboxQuery = ({ repo, doneWithinDays = 14, } = {}) => {
70
+ export const buildInboxQuery = ({ repo, doneWithinDays = 14, shape = "full", } = {}) => {
61
71
  const scope = repo ? `repo:${repo} ` : "";
62
72
  const owned = repo ? "" : "user:@me ";
63
73
  const doneSince = sinceDay(doneWithinDays);
74
+ /* Interpolated as empty strings rather than branching the query text, so the
75
+ two shapes cannot drift into two separately-maintained queries. */
76
+ const full = shape === "full";
77
+ const health = full ? PR_HEALTH : "";
78
+ const conversation = full ? PR_CONVERSATION : "";
79
+ const issueConversation = full ? ISSUE_CONVERSATION : "";
80
+ const issueLabels = full ? ISSUE_LABELS : "";
81
+ /*
82
+ * `rateLimit` is free — it does not count against itself — and it is the only
83
+ * authoritative source for what this query costs. Every estimate made about
84
+ * that on 2026-08-14 was wrong, one of them by 25x, because GraphQL cost is
85
+ * node-count based and nested connections multiply: `reviewThreads(first: 50)`
86
+ * beneath `search(first: 100)` is 5,000 nodes from two lines of query text. A
87
+ * package handing out a 111-point query should hand out the means to see it.
88
+ *
89
+ * (GraphQL has no `/* *\/` comments, only `#`, so this note lives out here.)
90
+ */
64
91
  return `
65
92
  {
93
+ rateLimit { cost nodeCount remaining resetAt }
66
94
  viewer { login }
67
95
  myPRs: search(query: "${scope}is:pr is:open author:@me", type: ISSUE, first: 100) {
68
96
  nodes { __typename ... on PullRequest {
69
- number title createdAt url headRefName
97
+ number title createdAt url headRefName isDraft
70
98
  repository { nameWithOwner }
71
99
  author { login }
72
- ${PR_HEALTH}
73
- ${PR_CONVERSATION}
100
+ ${health}
101
+ ${conversation}
74
102
  }}
75
103
  }
76
104
  reviewRequests: search(query: "${scope}is:pr is:open review-requested:@me", type: ISSUE, first: 20) {
77
105
  nodes { __typename ... on PullRequest {
78
- number title createdAt url headRefName
106
+ number title createdAt url headRefName isDraft
79
107
  repository { nameWithOwner }
80
108
  author { login }
81
- ${PR_HEALTH}
82
- ${PR_CONVERSATION}
109
+ ${health}
110
+ ${conversation}
83
111
  }}
84
112
  }
85
113
  reviewed: search(query: "${scope}is:pr is:open reviewed-by:@me -author:@me -review-requested:@me", type: ISSUE, first: 20) {
86
114
  nodes { __typename ... on PullRequest {
87
- number title createdAt url headRefName
115
+ number title createdAt url headRefName isDraft
88
116
  repository { nameWithOwner }
89
117
  author { login }
90
- ${PR_HEALTH}
91
- ${PR_CONVERSATION}
118
+ ${health}
119
+ ${conversation}
92
120
  }}
93
121
  }
94
122
  assigned: search(query: "${scope}is:open assignee:@me", type: ISSUE, first: 30) {
@@ -96,13 +124,13 @@ export const buildInboxQuery = ({ repo, doneWithinDays = 14, } = {}) => {
96
124
  __typename
97
125
  ... on Issue {
98
126
  number title createdAt url repository { nameWithOwner } author { login }
99
- ${ISSUE_CONVERSATION}
100
- ${ISSUE_LABELS}
127
+ ${issueConversation}
128
+ ${issueLabels}
101
129
  }
102
130
  ... on PullRequest {
103
- number title createdAt url headRefName repository { nameWithOwner } author { login }
104
- ${PR_HEALTH}
105
- ${PR_CONVERSATION}
131
+ number title createdAt url headRefName isDraft repository { nameWithOwner } author { login }
132
+ ${health}
133
+ ${conversation}
106
134
  }
107
135
  }
108
136
  }
@@ -111,8 +139,8 @@ export const buildInboxQuery = ({ repo, doneWithinDays = 14, } = {}) => {
111
139
  number title createdAt url
112
140
  repository { nameWithOwner }
113
141
  author { login }
114
- ${ISSUE_CONVERSATION}
115
- ${ISSUE_LABELS}
142
+ ${issueConversation}
143
+ ${issueLabels}
116
144
  }}
117
145
  }
118
146
  authoredIssues: search(query: "${scope}is:issue is:open author:@me archived:false", type: ISSUE, first: 30) {
@@ -120,17 +148,17 @@ export const buildInboxQuery = ({ repo, doneWithinDays = 14, } = {}) => {
120
148
  number title createdAt url
121
149
  repository { nameWithOwner }
122
150
  author { login }
123
- ${ISSUE_CONVERSATION}
124
- ${ISSUE_LABELS}
151
+ ${issueConversation}
152
+ ${issueLabels}
125
153
  }}
126
154
  }
127
155
  repoPRs: search(query: "${scope}${owned}is:pr is:open -author:@me archived:false", type: ISSUE, first: 30) {
128
156
  nodes { __typename ... on PullRequest {
129
- number title createdAt url headRefName
157
+ number title createdAt url headRefName isDraft
130
158
  repository { nameWithOwner }
131
159
  author { login }
132
- ${PR_HEALTH}
133
- ${PR_CONVERSATION}
160
+ ${health}
161
+ ${conversation}
134
162
  }}
135
163
  }
136
164
  recentlyDone: search(query: "${scope}is:pr author:@me -is:open closed:>=${doneSince}", type: ISSUE, first: 30) {
package/dist/inbox.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,qCAAqC;AACrC,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,uDAAuD;AAUvD,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,oEAAoE;AACpE,MAAM,SAAS,GAAG;;;;;YAKN,CAAA;AAEZ,6EAA6E;AAC7E,gFAAgF;AAChF,8EAA8E;AAC9E,2DAA2D;AAC3D,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,gFAAgF;AAChF,8EAA8E;AAC9E,+DAA+D;AAC/D,MAAM,eAAe,GAAG;;;;;;;8DAOsC,CAAA;AAE9D,MAAM,kBAAkB,GAAG;uFAC4D,CAAA;AAEvF,+EAA+E;AAC/E,qEAAqE;AACrE,MAAM,YAAY,GAAG;2CACsB,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAChC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAEvE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAC9B,IAAI,EACJ,cAAc,GAAG,EAAE,MACE,EAAE,EAAE,EAAE;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAA;IACrC,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAA;IAE1C,OAAO;;;0BAGiB,KAAK;;;;;QAKvB,SAAS;QACT,eAAe;;;mCAGY,KAAK;;;;;QAKhC,SAAS;QACT,eAAe;;;6BAGM,KAAK;;;;;QAK1B,SAAS;QACT,eAAe;;;6BAGM,KAAK;;;;;UAKxB,kBAAkB;UAClB,YAAY;;;;UAIZ,SAAS;UACT,eAAe;;;;+BAIM,KAAK,GAAG,KAAK;;;;;QAKpC,kBAAkB;QAClB,YAAY;;;mCAGe,KAAK;;;;;QAKhC,kBAAkB;QAClB,YAAY;;;4BAGQ,KAAK,GAAG,KAAK;;;;;QAKjC,SAAS;QACT,eAAe;;;iCAGU,KAAK,sCAAsC,SAAS;;;;;;;CAOpF,CAAA;AACD,CAAC,CAAA"}
1
+ {"version":3,"file":"inbox.js","sourceRoot":"","sources":["../src/inbox.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,qCAAqC;AACrC,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,uDAAuD;AAiCvD,2EAA2E;AAC3E,4EAA4E;AAC5E,4EAA4E;AAC5E,mEAAmE;AACnE,EAAE;AACF,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,8EAA8E;AAC9E,yBAAyB;AACzB,EAAE;AACF,2EAA2E;AAC3E,8EAA8E;AAC9E,2EAA2E;AAC3E,qBAAqB;AACrB,MAAM,SAAS,GAAG;;;;;YAKN,CAAA;AAEZ,6EAA6E;AAC7E,gFAAgF;AAChF,8EAA8E;AAC9E,2DAA2D;AAC3D,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,gFAAgF;AAChF,8EAA8E;AAC9E,+DAA+D;AAC/D,MAAM,eAAe,GAAG;;;;;;;8DAOsC,CAAA;AAE9D,MAAM,kBAAkB,GAAG;uFAC4D,CAAA;AAEvF,+EAA+E;AAC/E,qEAAqE;AACrE,MAAM,YAAY,GAAG;2CACsB,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAChC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAEvE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,EAC9B,IAAI,EACJ,cAAc,GAAG,EAAE,EACnB,KAAK,GAAG,MAAM,MACO,EAAE,EAAE,EAAE;IAC3B,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAA;IACrC,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAA;IAE1C;yEACqE;IACrE,MAAM,IAAI,GAAG,KAAK,KAAK,MAAM,CAAA;IAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAA;IACpC,MAAM,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAA;IAChD,MAAM,iBAAiB,GAAG,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAA;IACxD,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAA;IAE5C;;;;;;;;;OASG;IACH,OAAO;;;;0BAIiB,KAAK;;;;;QAKvB,MAAM;QACN,YAAY;;;mCAGe,KAAK;;;;;QAKhC,MAAM;QACN,YAAY;;;6BAGS,KAAK;;;;;QAK1B,MAAM;QACN,YAAY;;;6BAGS,KAAK;;;;;UAKxB,iBAAiB;UACjB,WAAW;;;;UAIX,MAAM;UACN,YAAY;;;;+BAIS,KAAK,GAAG,KAAK;;;;;QAKpC,iBAAiB;QACjB,WAAW;;;mCAGgB,KAAK;;;;;QAKhC,iBAAiB;QACjB,WAAW;;;4BAGS,KAAK,GAAG,KAAK;;;;;QAKjC,MAAM;QACN,YAAY;;;iCAGa,KAAK,sCAAsC,SAAS;;;;;;;CAOpF,CAAA;AACD,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kud/gh",
3
- "version": "0.4.1",
3
+ "version": "0.5.1",
4
4
  "description": "Surface-agnostic GitHub core — gh CLI primitives, PR review comments, health, and webhook logic.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",