@framers/agentos-ext-github 0.2.1 → 0.3.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.
Files changed (56) hide show
  1. package/README.md +49 -15
  2. package/dist/__tests__/app.test.d.ts +1 -1
  3. package/dist/__tests__/app.test.js +306 -2
  4. package/dist/__tests__/app.test.js.map +1 -1
  5. package/dist/__tests__/documents.test.js +28 -1
  6. package/dist/__tests__/documents.test.js.map +1 -1
  7. package/dist/__tests__/entry.test.d.ts +6 -0
  8. package/dist/__tests__/entry.test.d.ts.map +1 -0
  9. package/dist/__tests__/entry.test.js +17 -0
  10. package/dist/__tests__/entry.test.js.map +1 -0
  11. package/dist/__tests__/events.test.d.ts +8 -0
  12. package/dist/__tests__/events.test.d.ts.map +1 -0
  13. package/dist/__tests__/events.test.js +304 -0
  14. package/dist/__tests__/events.test.js.map +1 -0
  15. package/dist/app/credential.d.ts +4 -3
  16. package/dist/app/credential.d.ts.map +1 -1
  17. package/dist/app/credential.js +8 -4
  18. package/dist/app/credential.js.map +1 -1
  19. package/dist/app/events.d.ts +142 -0
  20. package/dist/app/events.d.ts.map +1 -0
  21. package/dist/app/events.js +182 -0
  22. package/dist/app/events.js.map +1 -0
  23. package/dist/app/http.d.ts +82 -13
  24. package/dist/app/http.d.ts.map +1 -1
  25. package/dist/app/http.js +151 -23
  26. package/dist/app/http.js.map +1 -1
  27. package/dist/app/installations.d.ts +8 -6
  28. package/dist/app/installations.d.ts.map +1 -1
  29. package/dist/app/installations.js +21 -15
  30. package/dist/app/installations.js.map +1 -1
  31. package/dist/app/reconcile.d.ts +25 -1
  32. package/dist/app/reconcile.d.ts.map +1 -1
  33. package/dist/app/reconcile.js +63 -26
  34. package/dist/app/reconcile.js.map +1 -1
  35. package/dist/app/userFlow.d.ts +10 -3
  36. package/dist/app/userFlow.d.ts.map +1 -1
  37. package/dist/app/userFlow.js +43 -4
  38. package/dist/app/userFlow.js.map +1 -1
  39. package/dist/app/webhook.d.ts +4 -1
  40. package/dist/app/webhook.d.ts.map +1 -1
  41. package/dist/app/webhook.js +6 -1
  42. package/dist/app/webhook.js.map +1 -1
  43. package/dist/documents/GitHubDocumentsConnector.d.ts +7 -4
  44. package/dist/documents/GitHubDocumentsConnector.d.ts.map +1 -1
  45. package/dist/documents/GitHubDocumentsConnector.js +39 -18
  46. package/dist/documents/GitHubDocumentsConnector.js.map +1 -1
  47. package/dist/documents/index.d.ts +12 -7
  48. package/dist/documents/index.d.ts.map +1 -1
  49. package/dist/documents/index.js +11 -6
  50. package/dist/documents/index.js.map +1 -1
  51. package/dist/index.d.ts +2 -1
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +2 -1
  54. package/dist/index.js.map +1 -1
  55. package/manifest.json +4 -2
  56. package/package.json +5 -4
package/dist/app/http.js CHANGED
@@ -1,21 +1,60 @@
1
1
  /**
2
- * @fileoverview GitHub's REST API over an injected `fetch`: one call with the headers every call carries, the pages
3
- * of a listing followed through its `Link` header, and the small readers that turn an answer into typed fields. An
4
- * answer outside 2xx throws {@link GitHubApiError}, whose message names the status and nothing of the answer's body.
2
+ * @fileoverview GitHub's REST API over an injected `fetch`: one call under a deadline with the headers every call
3
+ * carries, the pages of a listing followed through its `Link` header, and the small readers that turn an answer into
4
+ * typed fields. An answer outside 2xx throws {@link GitHubApiError}, whose message names the status and nothing of
5
+ * the answer's body and which says whether GitHub refused for a rate limit and when to try again; a call that gets no
6
+ * answer throws {@link GitHubNoAnswer}. An answer's body is read under a byte cap.
5
7
  * @module agentos-ext-github/app/http
6
8
  */
9
+ import { readCapped, ReadCapExceeded } from '@framers/agentos-ext-account-sources';
7
10
  /** The REST API version every call pins (`X-GitHub-Api-Version`). */
8
11
  export const GITHUB_API_VERSION = '2026-03-10';
9
12
  /** GitHub's REST API. */
10
13
  export const GITHUB_API_BASE = 'https://api.github.com';
11
- /** GitHub answered a status outside 2xx. The message holds the status alone, never the answer's body. */
14
+ /**
15
+ * How long one call may take when its caller sets no other deadline, its answer's body included: 30 seconds. GitHub
16
+ * ends a request it takes more than 10 seconds to process.
17
+ */
18
+ export const GITHUB_TIMEOUT_MS = 30_000;
19
+ /** The largest JSON answer read: 16 MiB, above the 7 MB at which GitHub cuts a recursive tree. */
20
+ export const GITHUB_MAX_ANSWER_BYTES = 16_777_216;
21
+ /** How much of a refused call's body is read to tell a rate limit from another 403. */
22
+ const REFUSAL_READ_BYTES = 16_384;
23
+ /** The wait after a rate limit that names no time: GitHub asks for at least a minute. */
24
+ const RATE_LIMIT_WAIT_MS = 60_000;
25
+ /**
26
+ * GitHub answered a status outside 2xx. The message holds the status alone, never the answer's body. `rateLimited`
27
+ * and `retryAt` follow GitHub's rules for a refused call
28
+ * (https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api#exceeding-the-rate-limit):
29
+ * `retry-after` first, then `x-ratelimit-reset` when `x-ratelimit-remaining` is 0, else a minute for a rate limit
30
+ * that names no time.
31
+ */
12
32
  export class GitHubApiError extends Error {
13
33
  status;
14
- /** @param status The HTTP status GitHub answered. */
15
- constructor(status) {
34
+ /** True when GitHub refused the call for a rate limit. */
35
+ rateLimited;
36
+ /** Epoch milliseconds before which the call should not be made again, or null when GitHub asked for no wait. */
37
+ retryAt;
38
+ /**
39
+ * @param status The HTTP status GitHub answered.
40
+ * @param timing Whether it was a rate limit and when to try again; neither when absent.
41
+ */
42
+ constructor(status, timing = {}) {
16
43
  super(`GitHub answered ${status}`);
17
44
  this.status = status;
18
45
  this.name = 'GitHubApiError';
46
+ this.rateLimited = timing.rateLimited ?? false;
47
+ this.retryAt = timing.retryAt ?? null;
48
+ }
49
+ }
50
+ /** A call that got no answer from GitHub, or whose answer's body stopped before its end. Names no address or cause. */
51
+ export class GitHubNoAnswer extends Error {
52
+ reason;
53
+ /** @param reason Why the call got no answer. */
54
+ constructor(reason) {
55
+ super(`GitHub gave no answer: ${reason}`);
56
+ this.reason = reason;
57
+ this.name = 'GitHubNoAnswer';
19
58
  }
20
59
  }
21
60
  /** The headers of a call: the bearer, the media type, the pinned API version and the pack's user agent. */
@@ -36,25 +75,114 @@ export async function discardBody(response) {
36
75
  // The body is not needed; a failed cancel changes nothing for the caller.
37
76
  }
38
77
  }
39
- /** Sends one call; an answer outside 2xx is discarded and thrown as {@link GitHubApiError}. */
78
+ /**
79
+ * The deadline of one call: its own timeout joined with the caller's signal, so the call and the reading of its body
80
+ * end at whichever comes first.
81
+ *
82
+ * @param signal The caller's signal, if any.
83
+ * @param timeoutMs How long the call may take; {@link GITHUB_TIMEOUT_MS} when absent.
84
+ */
85
+ export function callDeadline(signal, timeoutMs = GITHUB_TIMEOUT_MS) {
86
+ const timeout = AbortSignal.timeout(timeoutMs);
87
+ return {
88
+ signal: signal === undefined ? timeout : AbortSignal.any([signal, timeout]),
89
+ noAnswer: () => new GitHubNoAnswer(signal?.aborted === true ? 'cancelled' : timeout.aborted ? 'timeout' : 'network'),
90
+ };
91
+ }
92
+ /** True when a refused call's body names a rate limit, read up to {@link REFUSAL_READ_BYTES}; the body is never kept. */
93
+ async function bodyNamesRateLimit(response) {
94
+ let fields;
95
+ try {
96
+ fields = JSON.parse(new TextDecoder().decode(await readCapped(response, REFUSAL_READ_BYTES)));
97
+ }
98
+ catch {
99
+ // An unreadable or oversized refusal is told by its status and headers alone.
100
+ return false;
101
+ }
102
+ if (typeof fields !== 'object' || fields === null || Array.isArray(fields))
103
+ return false;
104
+ const { message, documentation_url: documentation } = fields;
105
+ return (typeof message === 'string' && /rate limit/i.test(message)) || (typeof documentation === 'string' && /rate-limit/i.test(documentation));
106
+ }
107
+ /**
108
+ * The time a `retry-after` header gives, as delay-seconds or an HTTP date, in epoch milliseconds, or null when it
109
+ * gives none that can be read. The wait is kept whole, however long: GitHub asks that no call be made before it ends.
110
+ * A date already past answers `now`.
111
+ */
112
+ function retryAfterAt(header, now) {
113
+ const value = (header ?? '').trim();
114
+ if (/^\d+$/.test(value)) {
115
+ const at = now + Number(value) * 1000;
116
+ return Number.isSafeInteger(at) ? at : null;
117
+ }
118
+ const date = value === '' ? Number.NaN : Date.parse(value);
119
+ return Number.isFinite(date) ? Math.max(date, now) : null;
120
+ }
121
+ /** The refusal an answer outside 2xx reads as: its status, whether it was a rate limit, and when to try again. */
122
+ async function refusalOf(response) {
123
+ const now = Date.now();
124
+ const retryAfter = retryAfterAt(response.headers.get('retry-after'), now);
125
+ const exhausted = response.headers.get('x-ratelimit-remaining')?.trim() === '0';
126
+ const reset = (response.headers.get('x-ratelimit-reset') ?? '').trim();
127
+ let rateLimited = response.status === 429 || (response.status === 403 && (retryAfter !== null || exhausted));
128
+ if (!rateLimited && response.status === 403)
129
+ rateLimited = await bodyNamesRateLimit(response);
130
+ else
131
+ await discardBody(response);
132
+ let retryAt = retryAfter;
133
+ if (retryAt === null && rateLimited) {
134
+ retryAt = exhausted && /^\d+$/.test(reset) ? Math.max(Number(reset) * 1000, now + 1000) : now + RATE_LIMIT_WAIT_MS;
135
+ }
136
+ return new GitHubApiError(response.status, { rateLimited, retryAt });
137
+ }
138
+ /**
139
+ * Sends one call under its deadline and the caller's signal. An answer outside 2xx is read only as far as telling a
140
+ * rate limit needs and thrown as {@link GitHubApiError}; a call that gets no answer throws {@link GitHubNoAnswer}.
141
+ */
40
142
  export async function githubSend(fetchImpl, url, request) {
41
143
  const headers = githubHeaders(request.token, request.accept);
42
144
  if (request.body !== undefined)
43
145
  headers['content-type'] = 'application/json';
44
- const response = await fetchImpl(url, { method: request.method ?? 'GET', headers, body: request.body, signal: request.signal });
45
- if (!response.ok) {
46
- await discardBody(response);
47
- throw new GitHubApiError(response.status);
146
+ const deadline = callDeadline(request.signal, request.timeoutMs);
147
+ let response;
148
+ try {
149
+ response = await fetchImpl(url, { method: request.method ?? 'GET', headers, body: request.body, signal: deadline.signal });
150
+ }
151
+ catch {
152
+ // The fetch's own error would name the address or its cause; the reason is all a caller acts on.
153
+ throw deadline.noAnswer();
48
154
  }
49
- return response;
155
+ if (!response.ok)
156
+ throw await refusalOf(response);
157
+ return {
158
+ response,
159
+ async bytes(maxBytes) {
160
+ try {
161
+ return await readCapped(response, maxBytes);
162
+ }
163
+ catch (error) {
164
+ if (error instanceof ReadCapExceeded)
165
+ throw error;
166
+ throw deadline.noAnswer();
167
+ }
168
+ },
169
+ };
50
170
  }
51
- /** An answer's JSON, or `undefined` when it has no body (a 202 or a 204). */
52
- async function answerOf(response) {
53
- const text = await response.text();
54
- if (text === '')
171
+ /** An answer's JSON, read under {@link GITHUB_MAX_ANSWER_BYTES}, or `undefined` when it has no body (a 202 or a 204). */
172
+ async function answerOf(answer) {
173
+ let bytes;
174
+ try {
175
+ bytes = await answer.bytes(GITHUB_MAX_ANSWER_BYTES);
176
+ }
177
+ catch (error) {
178
+ if (error instanceof ReadCapExceeded)
179
+ throw new Error('GitHub answered a body past the size cap');
180
+ throw error;
181
+ }
182
+ if (bytes.byteLength === 0)
55
183
  return undefined;
56
184
  try {
57
- return JSON.parse(text);
185
+ return JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(bytes));
58
186
  }
59
187
  catch {
60
188
  // The parser's message would quote the body.
@@ -64,7 +192,7 @@ async function answerOf(response) {
64
192
  /**
65
193
  * One call to `url` (an address under the API's base), answering its JSON, or `undefined` for an answer without a
66
194
  * body. Sends `Authorization: Bearer <token>`, `Accept: application/vnd.github+json` unless told otherwise,
67
- * `X-GitHub-Api-Version` and `User-Agent`.
195
+ * `X-GitHub-Api-Version` and `User-Agent`, and ends at its deadline.
68
196
  */
69
197
  export async function githubFetch(fetchImpl, url, request) {
70
198
  return answerOf(await githubSend(fetchImpl, url, request));
@@ -77,16 +205,16 @@ export function nextPage(link) {
77
205
  return match === null ? null : match[1];
78
206
  }
79
207
  /**
80
- * Each page of a listing as its JSON, following `Link: <...>; rel="next"`. A next page on another origin than the
81
- * first is refused, so the bearer never leaves the API it was sent to.
208
+ * Each page of a listing as its JSON, following `Link: <...>; rel="next"`, each page under a deadline of its own. A
209
+ * next page on another origin than the first is refused, so the bearer never leaves the API it was sent to.
82
210
  */
83
211
  export async function* githubPages(fetchImpl, url, request) {
84
212
  const origin = new URL(url).origin;
85
213
  let next = url;
86
214
  while (next !== null) {
87
- const response = await githubSend(fetchImpl, next, request);
88
- const link = response.headers.get('link');
89
- yield await answerOf(response);
215
+ const answer = await githubSend(fetchImpl, next, request);
216
+ const link = answer.response.headers.get('link');
217
+ yield await answerOf(answer);
90
218
  next = nextPage(link);
91
219
  if (next !== null && new URL(next).origin !== origin)
92
220
  throw new Error('GitHub named a next page on another origin');
@@ -1 +1 @@
1
- {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/app/http.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,qEAAqE;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAE/C,yBAAyB;AACzB,MAAM,CAAC,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAmBxD,yGAAyG;AACzG,MAAM,OAAO,cAAe,SAAQ,KAAK;IAElB;IADrB,qDAAqD;IACrD,YAAqB,MAAc;QACjC,KAAK,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;QADhB,WAAM,GAAN,MAAM,CAAQ;QAEjC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,2GAA2G;AAC3G,MAAM,UAAU,aAAa,CAAC,KAAa,EAAE,MAAM,GAAG,6BAA6B;IACjF,OAAO;QACL,aAAa,EAAE,UAAU,KAAK,EAAE;QAChC,MAAM;QACN,sBAAsB,EAAE,kBAAkB;QAC1C,YAAY,EAAE,oBAAoB;KACnC,CAAC;AACJ,CAAC;AAED,wCAAwC;AACxC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAkB;IAClD,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;IAC5E,CAAC;AACH,CAAC;AAED,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,SAAuB,EAAE,GAAW,EAAE,OAAsB;IAC3F,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;IAC7E,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAChI,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5B,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,6EAA6E;AAC7E,KAAK,UAAU,QAAQ,CAAC,QAAkB;IACxC,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,IAAI,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAClC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,6CAA6C;QAC7C,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC7D,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAAuB,EAAE,GAAW,EAAE,OAAsB;IAC5F,OAAO,QAAQ,CAAC,MAAM,UAAU,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,QAAQ,CAAC,IAAmB;IAC1C,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,MAAM,KAAK,GAAG,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,WAAW,CAAC,SAAuB,EAAE,GAAW,EAAE,OAAsB;IAC7F,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACnC,IAAI,IAAI,GAAkB,GAAG,CAAC;IAC9B,OAAO,IAAI,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5D,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC/B,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IACtH,CAAC;AACH,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,KAAK,CAAI,SAAuB,EAAE,GAAW,EAAE,OAAsB,EAAE,KAAsC;IACjI,MAAM,GAAG,GAAQ,EAAE,CAAC;IACpB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,WAAW,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IACxF,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,KAAc,EAAE,IAAY;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,wBAAwB,CAAC,CAAC;IAC1I,OAAO,KAAgC,CAAC;AAC1C,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,OAAO,CAAC,KAAc,EAAE,IAAY;IAClD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,qBAAqB,CAAC,CAAC;IACzF,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,QAAQ,CAAC,MAA+B,EAAE,GAAW,EAAE,IAAY;IACjF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,qBAAqB,GAAG,EAAE,CAAC,CAAC;IAClG,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,QAAQ,CAAC,MAA+B,EAAE,GAAW,EAAE,IAAY;IACjF,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,IAAI,qBAAqB,GAAG,EAAE,CAAC,CAAC;IAClG,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/app/http.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview What a user token reaches (the person, the app's installations they can access, and the repositories
3
3
  * of an installation they can read) and the app's own installations, listed and removed with its JWT. Each function
4
- * takes `{ fetchImpl, apiBase }` last: `fetch` and GitHub's API when absent.
4
+ * takes `{ fetchImpl, apiBase, timeoutMs }` last: `fetch`, GitHub's API and a 30-second deadline a call when absent.
5
5
  * @module agentos-ext-github/app/installations
6
6
  */
7
7
  import type { GitHubAppCredential } from './credential.js';
@@ -27,22 +27,24 @@ export interface UserRepository {
27
27
  fullName: string;
28
28
  defaultBranch: string;
29
29
  }
30
+ /** An installation's account, as GitHub answers it and as a webhook event names it: a login, or an enterprise's slug, and an id. */
31
+ export declare function accountOf(value: unknown): GitHubAccount;
30
32
  /** The person a user token belongs to (`GET /user`): the GitHub id and login a link is made by. */
31
- export declare function userOfToken(token: string, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<GitHubUser>;
33
+ export declare function userOfToken(token: string, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<GitHubUser>;
32
34
  /** The app's installations a user token can access (`GET /user/installations`), across every page. */
33
- export declare function installationsOfUser(token: string, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
35
+ export declare function installationsOfUser(token: string, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
34
36
  /**
35
37
  * The repositories of an installation a user token can read (`GET /user/installations/{installation_id}/repositories`),
36
38
  * across every page: those the person owns, collaborates on or reaches through an organization.
37
39
  */
38
- export declare function repositoriesOfUser(token: string, installationId: number, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<UserRepository[]>;
40
+ export declare function repositoriesOfUser(token: string, installationId: number, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<UserRepository[]>;
39
41
  /** Every installation of the app (`GET /app/installations`, with its JWT), across every page. */
40
- export declare function listInstallations(credential: GitHubAppCredential, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
42
+ export declare function listInstallations(credential: GitHubAppCredential, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<GitHubInstallation[]>;
41
43
  /**
42
44
  * Uninstalls the app from an account (`DELETE /app/installations/{installation_id}`, with its JWT). Answers without an
43
45
  * error on 202 (GitHub has taken the removal and does it in the background at the pinned API version), on 204 (the
44
46
  * answer of earlier versions) and on 404 (the installation is already gone); the answer's body is never read. Any
45
47
  * other status throws {@link GitHubApiError}.
46
48
  */
47
- export declare function uninstallInstallation(credential: GitHubAppCredential, installationId: number, { fetchImpl, apiBase }?: GitHubApiOptions): Promise<void>;
49
+ export declare function uninstallInstallation(credential: GitHubAppCredential, installationId: number, { fetchImpl, apiBase, timeoutMs }?: GitHubApiOptions): Promise<void>;
48
50
  //# sourceMappingURL=installations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAEnB,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uGAAuG;AACvG,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;CAC/B;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAcD,mGAAmG;AACnG,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAiB,EAAE,OAAyB,EAAE,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAG7I;AAED,sGAAsG;AACtG,wBAAsB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAiB,EAAE,OAAyB,EAAE,GAAE,gBAAqB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAI/J;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,EAAE,SAAiB,EAAE,OAAyB,EAAE,GAAE,gBAAqB,GACtE,OAAO,CAAC,cAAc,EAAE,CAAC,CAW3B;AAED,iGAAiG;AACjG,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,mBAAmB,EAAE,EAAE,SAAiB,EAAE,OAAyB,EAAE,GAAE,gBAAqB,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAE/K;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,mBAAmB,EAC/B,cAAc,EAAE,MAAM,EACtB,EAAE,SAAiB,EAAE,OAAyB,EAAE,GAAE,gBAAqB,GACtE,OAAO,CAAC,IAAI,CAAC,CAOf"}
1
+ {"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAEnB,0CAA0C;AAC1C,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,uGAAuG;AACvG,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,4EAA4E;AAC5E,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;CAC/B;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,oIAAoI;AACpI,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAMvD;AAUD,mGAAmG;AACnG,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC,CAGxJ;AAED,sGAAsG;AACtG,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,MAAM,EACb,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAI/B;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,cAAc,EAAE,CAAC,CAW3B;AAED,iGAAiG;AACjG,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,mBAAmB,EAC/B,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAI/B;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,mBAAmB,EAC/B,cAAc,EAAE,MAAM,EACtB,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,GAAE,gBAAqB,GACjF,OAAO,CAAC,IAAI,CAAC,CAQf"}
@@ -1,35 +1,40 @@
1
1
  /**
2
2
  * @fileoverview What a user token reaches (the person, the app's installations they can access, and the repositories
3
3
  * of an installation they can read) and the app's own installations, listed and removed with its JWT. Each function
4
- * takes `{ fetchImpl, apiBase }` last: `fetch` and GitHub's API when absent.
4
+ * takes `{ fetchImpl, apiBase, timeoutMs }` last: `fetch`, GitHub's API and a 30-second deadline a call when absent.
5
5
  * @module agentos-ext-github/app/installations
6
6
  */
7
7
  import { arrayOf, discardBody, GITHUB_API_BASE, GitHubApiError, githubFetch, githubSend, numberIn, objectOf, pages, stringIn, } from './http.js';
8
+ /** An installation's account, as GitHub answers it and as a webhook event names it: a login, or an enterprise's slug, and an id. */
9
+ export function accountOf(value) {
10
+ const account = objectOf(value, "an installation's account");
11
+ return {
12
+ login: stringIn(account, typeof account.login === 'string' ? 'login' : 'slug', "an installation's account"),
13
+ id: numberIn(account, 'id', "an installation's account"),
14
+ };
15
+ }
8
16
  function installationOf(value) {
9
17
  const fields = objectOf(value, 'an installation');
10
- const account = fields.account === null || fields.account === undefined ? null : objectOf(fields.account, "an installation's account");
11
18
  return {
12
19
  id: numberIn(fields, 'id', 'an installation'),
13
- account: account === null
14
- ? null
15
- : { login: stringIn(account, typeof account.login === 'string' ? 'login' : 'slug', "an installation's account"), id: numberIn(account, 'id', "an installation's account") },
20
+ account: fields.account === null || fields.account === undefined ? null : accountOf(fields.account),
16
21
  };
17
22
  }
18
23
  /** The person a user token belongs to (`GET /user`): the GitHub id and login a link is made by. */
19
- export async function userOfToken(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
20
- const user = objectOf(await githubFetch(fetchImpl, `${apiBase}/user`, { token }), 'a user');
24
+ export async function userOfToken(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
25
+ const user = objectOf(await githubFetch(fetchImpl, `${apiBase}/user`, { token, timeoutMs }), 'a user');
21
26
  return { id: numberIn(user, 'id', 'a user'), login: stringIn(user, 'login', 'a user') };
22
27
  }
23
28
  /** The app's installations a user token can access (`GET /user/installations`), across every page. */
24
- export async function installationsOfUser(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
25
- return pages(fetchImpl, `${apiBase}/user/installations?per_page=100`, { token }, (page) => arrayOf(objectOf(page, 'a page of installations').installations, 'a page of installations').map(installationOf));
29
+ export async function installationsOfUser(token, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
30
+ return pages(fetchImpl, `${apiBase}/user/installations?per_page=100`, { token, timeoutMs }, (page) => arrayOf(objectOf(page, 'a page of installations').installations, 'a page of installations').map(installationOf));
26
31
  }
27
32
  /**
28
33
  * The repositories of an installation a user token can read (`GET /user/installations/{installation_id}/repositories`),
29
34
  * across every page: those the person owns, collaborates on or reaches through an organization.
30
35
  */
31
- export async function repositoriesOfUser(token, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
32
- return pages(fetchImpl, `${apiBase}/user/installations/${installationId}/repositories?per_page=100`, { token }, (page) => arrayOf(objectOf(page, 'a page of repositories').repositories, 'a page of repositories').map((value) => {
36
+ export async function repositoriesOfUser(token, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
37
+ return pages(fetchImpl, `${apiBase}/user/installations/${installationId}/repositories?per_page=100`, { token, timeoutMs }, (page) => arrayOf(objectOf(page, 'a page of repositories').repositories, 'a page of repositories').map((value) => {
33
38
  const repository = objectOf(value, 'a repository');
34
39
  return {
35
40
  id: numberIn(repository, 'id', 'a repository'),
@@ -39,8 +44,8 @@ export async function repositoriesOfUser(token, installationId, { fetchImpl = fe
39
44
  }));
40
45
  }
41
46
  /** Every installation of the app (`GET /app/installations`, with its JWT), across every page. */
42
- export async function listInstallations(credential, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
43
- return pages(fetchImpl, `${apiBase}/app/installations?per_page=100`, { token: credential.jwt() }, (page) => arrayOf(page, 'a page of installations').map(installationOf));
47
+ export async function listInstallations(credential, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
48
+ return pages(fetchImpl, `${apiBase}/app/installations?per_page=100`, { token: credential.jwt(), timeoutMs }, (page) => arrayOf(page, 'a page of installations').map(installationOf));
44
49
  }
45
50
  /**
46
51
  * Uninstalls the app from an account (`DELETE /app/installations/{installation_id}`, with its JWT). Answers without an
@@ -48,9 +53,10 @@ export async function listInstallations(credential, { fetchImpl = fetch, apiBase
48
53
  * answer of earlier versions) and on 404 (the installation is already gone); the answer's body is never read. Any
49
54
  * other status throws {@link GitHubApiError}.
50
55
  */
51
- export async function uninstallInstallation(credential, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE } = {}) {
56
+ export async function uninstallInstallation(credential, installationId, { fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs } = {}) {
52
57
  try {
53
- await discardBody(await githubSend(fetchImpl, `${apiBase}/app/installations/${installationId}`, { token: credential.jwt(), method: 'DELETE' }));
58
+ const answer = await githubSend(fetchImpl, `${apiBase}/app/installations/${installationId}`, { token: credential.jwt(), method: 'DELETE', timeoutMs });
59
+ await discardBody(answer.response);
54
60
  }
55
61
  catch (error) {
56
62
  if (error instanceof GitHubApiError && error.status === 404)
@@ -1 +1 @@
1
- {"version":3,"file":"installations.js","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"installations.js","sourceRoot":"","sources":["../../src/app/installations.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,21 @@ export interface UnreadableRepository {
35
35
  repositoryId: number;
36
36
  status: number;
37
37
  }
38
+ /** A redelivery GitHub refused: the delivery asked for again, and GitHub's status. */
39
+ export interface RefusedRedelivery {
40
+ id: number;
41
+ status: number;
42
+ }
43
+ /** How the deliveries step went, beside the deliveries it asked for again. */
44
+ export interface DeliveriesOutcome {
45
+ /**
46
+ * GitHub's status when it answered the deliveries listing outside 2xx, in which case no delivery was asked for
47
+ * again; null when the listing was read.
48
+ */
49
+ listingStatus: number | null;
50
+ /** Redeliveries GitHub refused; the others were still asked for. */
51
+ refused: RefusedRedelivery[];
52
+ }
38
53
  /** What the reconcile found and did. */
39
54
  export interface ReconcileOutcome {
40
55
  /** Installations the app no longer has: their links and documents go. */
@@ -45,6 +60,8 @@ export interface ReconcileOutcome {
45
60
  redelivered: number[];
46
61
  /** Repositories whose head GitHub would not answer. */
47
62
  unreadable: UnreadableRepository[];
63
+ /** GitHub's refusals in the deliveries step, which never cost the lists above. */
64
+ deliveries: DeliveriesOutcome;
48
65
  }
49
66
  /**
50
67
  * Reconciles a GitHub App's links with GitHub: an installation that `GET /app/installations` no longer lists is
@@ -53,9 +70,16 @@ export interface ReconcileOutcome {
53
70
  * status; and the app's failed deliveries since `since` are asked for again
54
71
  * (`GET /app/hook/deliveries`, `POST /app/hook/deliveries/{delivery_id}/attempts`).
55
72
  *
73
+ * A delivery whose newest attempt the receiving route answered with a 4xx other than 408 and 429 is not asked for
74
+ * again: such a refusal (a body past the route's size cap, a 413) answers the same on every attempt, and each new
75
+ * attempt would bring the delivery into the next run's window again, while a 408 or a 429 is a transient the route
76
+ * itself asks to be sent again. GitHub's refusals in the deliveries step never cost the lists computed before it: a
77
+ * deliveries listing GitHub answers outside 2xx is answered as `deliveries.listingStatus`, with no delivery asked for
78
+ * again, and a redelivery it refuses as an entry of `deliveries.refused`, the others still asked for.
79
+ *
56
80
  * @param credential The app's credential.
57
81
  * @param links The links to check.
58
82
  * @param options The window's start, and where calls go.
59
83
  */
60
- export declare function reconcileInstallations(credential: GitHubAppCredential, links: readonly ReconcileLink[], { since, fetchImpl, apiBase }: ReconcileOptions): Promise<ReconcileOutcome>;
84
+ export declare function reconcileInstallations(credential: GitHubAppCredential, links: readonly ReconcileLink[], { since, fetchImpl, apiBase, timeoutMs }: ReconcileOptions): Promise<ReconcileOutcome>;
61
85
  //# sourceMappingURL=reconcile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/app/reconcile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAGnB,2GAA2G;AAC3G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC9C;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD,iFAAiF;IACjF,KAAK,EAAE,IAAI,CAAC;CACb;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8HAA8H;AAC9H,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6EAA6E;IAC7E,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,kCAAkC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,uDAAuD;IACvD,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC;AAgDD;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,mBAAmB,EAC/B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,EAAE,KAAK,EAAE,SAAiB,EAAE,OAAyB,EAAE,EAAE,gBAAgB,GACxE,OAAO,CAAC,gBAAgB,CAAC,CA2B3B"}
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/app/reconcile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAWL,KAAK,gBAAgB,EACtB,MAAM,WAAW,CAAC;AAGnB,2GAA2G;AAC3G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,oEAAoE;AACpE,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,SAAS,mBAAmB,EAAE,CAAC;CAC9C;AAED,+DAA+D;AAC/D,MAAM,WAAW,gBAAiB,SAAQ,gBAAgB;IACxD,iFAAiF;IACjF,KAAK,EAAE,IAAI,CAAC;CACb;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8HAA8H;AAC9H,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,sFAAsF;AACtF,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,oEAAoE;IACpE,OAAO,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,wCAAwC;AACxC,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6EAA6E;IAC7E,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,kCAAkC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,uDAAuD;IACvD,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,kFAAkF;IAClF,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAkFD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,mBAAmB,EAC/B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,EAAE,KAAK,EAAE,SAAiB,EAAE,OAAyB,EAAE,SAAS,EAAE,EAAE,gBAAgB,GACnF,OAAO,CAAC,gBAAgB,CAAC,CA2B3B"}
@@ -5,46 +5,76 @@
5
5
  */
6
6
  import { arrayOf, discardBody, GITHUB_API_BASE, GitHubApiError, githubFetch, githubPages, githubSend, numberIn, objectOf, stringIn, } from './http.js';
7
7
  import { listInstallations } from './installations.js';
8
+ /** The client refusals a retry may change: a timeout (408) and a rate limit (429). */
9
+ const TRANSIENT_CLIENT_STATUSES = new Set([408, 429]);
8
10
  function deliveryOf(value) {
9
11
  const fields = objectOf(value, 'a delivery');
10
- const status = numberIn(fields, 'status_code', 'a delivery');
11
12
  return {
12
13
  id: numberIn(fields, 'id', 'a delivery'),
13
14
  guid: stringIn(fields, 'guid', 'a delivery'),
14
15
  deliveredAt: Date.parse(stringIn(fields, 'delivered_at', 'a delivery')),
15
- succeeded: status >= 200 && status < 300,
16
+ statusCode: numberIn(fields, 'status_code', 'a delivery'),
16
17
  };
17
18
  }
19
+ /** True when the route took the attempt (a 2xx). */
20
+ function delivered(attempt) {
21
+ return attempt.statusCode >= 200 && attempt.statusCode < 300;
22
+ }
23
+ /** True when the route refused the attempt with a 4xx a retry would not change: any but 408 and 429. */
24
+ function refusedByRoute(attempt) {
25
+ return attempt.statusCode >= 400 && attempt.statusCode < 500 && !TRANSIENT_CLIENT_STATUSES.has(attempt.statusCode);
26
+ }
18
27
  /**
19
28
  * Asks again for each event of the window that no attempt delivered: the newest failed attempt of each delivery guid
20
- * with no successful attempt since `since`. GitHub lists deliveries newest first, so the pages stop at the first one
21
- * that reaches past the window.
29
+ * with no successful attempt since `since`, unless the route refused that newest attempt with a 4xx other than 408 and
30
+ * 429. GitHub lists deliveries newest first, so the pages stop at the first one that reaches past the window. A
31
+ * listing GitHub answers outside 2xx asks for nothing again and answers GitHub's status, since a guid's other attempts
32
+ * may lie on the pages not read; a redelivery GitHub refuses is answered with its status, and the others are still
33
+ * asked for.
22
34
  */
23
- async function redeliverFailures(credential, since, fetchImpl, apiBase) {
35
+ async function redeliverFailures(credential, since, fetchImpl, apiBase, timeoutMs) {
24
36
  const attemptsByGuid = new Map();
25
- for await (const page of githubPages(fetchImpl, `${apiBase}/app/hook/deliveries?per_page=100`, { token: credential.jwt() })) {
26
- const deliveries = arrayOf(page, 'a page of deliveries').map(deliveryOf);
27
- for (const delivery of deliveries) {
28
- if (!(delivery.deliveredAt >= since))
29
- continue;
30
- const attempts = attemptsByGuid.get(delivery.guid);
31
- if (attempts === undefined)
32
- attemptsByGuid.set(delivery.guid, [delivery]);
33
- else
34
- attempts.push(delivery);
37
+ try {
38
+ for await (const page of githubPages(fetchImpl, `${apiBase}/app/hook/deliveries?per_page=100`, { token: credential.jwt(), timeoutMs })) {
39
+ const deliveries = arrayOf(page, 'a page of deliveries').map(deliveryOf);
40
+ for (const delivery of deliveries) {
41
+ if (!(delivery.deliveredAt >= since))
42
+ continue;
43
+ const attempts = attemptsByGuid.get(delivery.guid);
44
+ if (attempts === undefined)
45
+ attemptsByGuid.set(delivery.guid, [delivery]);
46
+ else
47
+ attempts.push(delivery);
48
+ }
49
+ if (deliveries.length === 0 || deliveries.some((delivery) => !(delivery.deliveredAt >= since)))
50
+ break;
35
51
  }
36
- if (deliveries.length === 0 || deliveries.some((delivery) => !(delivery.deliveredAt >= since)))
37
- break;
52
+ }
53
+ catch (error) {
54
+ if (!(error instanceof GitHubApiError))
55
+ throw error;
56
+ return { redelivered: [], deliveries: { listingStatus: error.status, refused: [] } };
38
57
  }
39
58
  const redelivered = [];
59
+ const refused = [];
40
60
  for (const attempts of attemptsByGuid.values()) {
41
- if (attempts.some((attempt) => attempt.succeeded))
61
+ if (attempts.some(delivered))
42
62
  continue;
43
63
  const latest = attempts.reduce((newest, attempt) => (attempt.deliveredAt > newest.deliveredAt ? attempt : newest));
44
- await discardBody(await githubSend(fetchImpl, `${apiBase}/app/hook/deliveries/${latest.id}/attempts`, { token: credential.jwt(), method: 'POST' }));
45
- redelivered.push(latest.id);
64
+ if (refusedByRoute(latest))
65
+ continue;
66
+ try {
67
+ const answer = await githubSend(fetchImpl, `${apiBase}/app/hook/deliveries/${latest.id}/attempts`, { token: credential.jwt(), method: 'POST', timeoutMs });
68
+ await discardBody(answer.response);
69
+ redelivered.push(latest.id);
70
+ }
71
+ catch (error) {
72
+ if (!(error instanceof GitHubApiError))
73
+ throw error;
74
+ refused.push({ id: latest.id, status: error.status });
75
+ }
46
76
  }
47
- return redelivered;
77
+ return { redelivered, deliveries: { listingStatus: null, refused } };
48
78
  }
49
79
  /**
50
80
  * Reconciles a GitHub App's links with GitHub: an installation that `GET /app/installations` no longer lists is
@@ -53,12 +83,19 @@ async function redeliverFailures(credential, since, fetchImpl, apiBase) {
53
83
  * status; and the app's failed deliveries since `since` are asked for again
54
84
  * (`GET /app/hook/deliveries`, `POST /app/hook/deliveries/{delivery_id}/attempts`).
55
85
  *
86
+ * A delivery whose newest attempt the receiving route answered with a 4xx other than 408 and 429 is not asked for
87
+ * again: such a refusal (a body past the route's size cap, a 413) answers the same on every attempt, and each new
88
+ * attempt would bring the delivery into the next run's window again, while a 408 or a 429 is a transient the route
89
+ * itself asks to be sent again. GitHub's refusals in the deliveries step never cost the lists computed before it: a
90
+ * deliveries listing GitHub answers outside 2xx is answered as `deliveries.listingStatus`, with no delivery asked for
91
+ * again, and a redelivery it refuses as an entry of `deliveries.refused`, the others still asked for.
92
+ *
56
93
  * @param credential The app's credential.
57
94
  * @param links The links to check.
58
95
  * @param options The window's start, and where calls go.
59
96
  */
60
- export async function reconcileInstallations(credential, links, { since, fetchImpl = fetch, apiBase = GITHUB_API_BASE }) {
61
- const listed = new Set((await listInstallations(credential, { fetchImpl, apiBase })).map((installation) => installation.id));
97
+ export async function reconcileInstallations(credential, links, { since, fetchImpl = fetch, apiBase = GITHUB_API_BASE, timeoutMs }) {
98
+ const listed = new Set((await listInstallations(credential, { fetchImpl, apiBase, timeoutMs })).map((installation) => installation.id));
62
99
  const gone = [];
63
100
  const moved = [];
64
101
  const unreadable = [];
@@ -71,7 +108,7 @@ export async function reconcileInstallations(credential, links, { since, fetchIm
71
108
  for (const repository of link.repositories) {
72
109
  try {
73
110
  const token = await credential.installationToken(link.installationId, [repository.id]);
74
- const branch = objectOf(await githubFetch(fetchImpl, `${apiBase}/repos/${repository.fullName}/branches/${encodeURIComponent(repository.defaultBranch)}`, { token }), 'a branch');
111
+ const branch = objectOf(await githubFetch(fetchImpl, `${apiBase}/repos/${repository.fullName}/branches/${encodeURIComponent(repository.defaultBranch)}`, { token, timeoutMs }), 'a branch');
75
112
  const head = stringIn(objectOf(branch.commit, "a branch's commit"), 'sha', "a branch's commit");
76
113
  if (head !== repository.headCommit)
77
114
  moved.push({ installationId: link.installationId, repositoryId: repository.id, head });
@@ -83,7 +120,7 @@ export async function reconcileInstallations(credential, links, { since, fetchIm
83
120
  }
84
121
  }
85
122
  }
86
- const redelivered = await redeliverFailures(credential, since.getTime(), fetchImpl, apiBase);
87
- return { gone, moved, redelivered, unreadable };
123
+ const { redelivered, deliveries } = await redeliverFailures(credential, since.getTime(), fetchImpl, apiBase, timeoutMs);
124
+ return { gone, moved, redelivered, unreadable, deliveries };
88
125
  }
89
126
  //# sourceMappingURL=reconcile.js.map