ghagga-forge 3.1.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 (77) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/adapters/github/github-app-credential-provider.d.ts +102 -0
  4. package/dist/adapters/github/github-app-credential-provider.d.ts.map +1 -0
  5. package/dist/adapters/github/github-app-credential-provider.js +166 -0
  6. package/dist/adapters/github/github-app-credential-provider.js.map +1 -0
  7. package/dist/adapters/github/github-client-port.d.ts +92 -0
  8. package/dist/adapters/github/github-client-port.d.ts.map +1 -0
  9. package/dist/adapters/github/github-client-port.js +24 -0
  10. package/dist/adapters/github/github-client-port.js.map +1 -0
  11. package/dist/adapters/github/github-forge-adapter.d.ts +105 -0
  12. package/dist/adapters/github/github-forge-adapter.d.ts.map +1 -0
  13. package/dist/adapters/github/github-forge-adapter.js +225 -0
  14. package/dist/adapters/github/github-forge-adapter.js.map +1 -0
  15. package/dist/adapters/github/static-token-provider.d.ts +30 -0
  16. package/dist/adapters/github/static-token-provider.d.ts.map +1 -0
  17. package/dist/adapters/github/static-token-provider.js +35 -0
  18. package/dist/adapters/github/static-token-provider.js.map +1 -0
  19. package/dist/adapters/gitlab/gitlab-client-port.d.ts +82 -0
  20. package/dist/adapters/gitlab/gitlab-client-port.d.ts.map +1 -0
  21. package/dist/adapters/gitlab/gitlab-client-port.js +27 -0
  22. package/dist/adapters/gitlab/gitlab-client-port.js.map +1 -0
  23. package/dist/adapters/gitlab/gitlab-forge-adapter.d.ts +118 -0
  24. package/dist/adapters/gitlab/gitlab-forge-adapter.d.ts.map +1 -0
  25. package/dist/adapters/gitlab/gitlab-forge-adapter.js +238 -0
  26. package/dist/adapters/gitlab/gitlab-forge-adapter.js.map +1 -0
  27. package/dist/comment-id.d.ts +45 -0
  28. package/dist/comment-id.d.ts.map +1 -0
  29. package/dist/comment-id.js +48 -0
  30. package/dist/comment-id.js.map +1 -0
  31. package/dist/errors.d.ts +48 -0
  32. package/dist/errors.d.ts.map +1 -0
  33. package/dist/errors.js +67 -0
  34. package/dist/errors.js.map +1 -0
  35. package/dist/index.d.ts +35 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +34 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/lint-boundary.d.ts +36 -0
  40. package/dist/lint-boundary.d.ts.map +1 -0
  41. package/dist/lint-boundary.impl.d.mts +41 -0
  42. package/dist/lint-boundary.impl.mjs +400 -0
  43. package/dist/lint-boundary.js +35 -0
  44. package/dist/lint-boundary.js.map +1 -0
  45. package/dist/ports/ci-runner.d.ts +48 -0
  46. package/dist/ports/ci-runner.d.ts.map +1 -0
  47. package/dist/ports/ci-runner.js +10 -0
  48. package/dist/ports/ci-runner.js.map +1 -0
  49. package/dist/ports/credential-provider.d.ts +32 -0
  50. package/dist/ports/credential-provider.d.ts.map +1 -0
  51. package/dist/ports/credential-provider.js +10 -0
  52. package/dist/ports/credential-provider.js.map +1 -0
  53. package/dist/ports/forge-adapter.d.ts +174 -0
  54. package/dist/ports/forge-adapter.d.ts.map +1 -0
  55. package/dist/ports/forge-adapter.js +34 -0
  56. package/dist/ports/forge-adapter.js.map +1 -0
  57. package/dist/ports/webhook-codec.d.ts +41 -0
  58. package/dist/ports/webhook-codec.d.ts.map +1 -0
  59. package/dist/ports/webhook-codec.js +18 -0
  60. package/dist/ports/webhook-codec.js.map +1 -0
  61. package/dist/project.d.ts +32 -0
  62. package/dist/project.d.ts.map +1 -0
  63. package/dist/project.js +41 -0
  64. package/dist/project.js.map +1 -0
  65. package/dist/ref.d.ts +20 -0
  66. package/dist/ref.d.ts.map +1 -0
  67. package/dist/ref.js +21 -0
  68. package/dist/ref.js.map +1 -0
  69. package/dist/registry.d.ts +69 -0
  70. package/dist/registry.d.ts.map +1 -0
  71. package/dist/registry.js +68 -0
  72. package/dist/registry.js.map +1 -0
  73. package/dist/types.d.ts +310 -0
  74. package/dist/types.d.ts.map +1 -0
  75. package/dist/types.js +50 -0
  76. package/dist/types.js.map +1 -0
  77. package/package.json +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 JNZader & Gentleman Programming Community
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # ghagga-forge
2
+
3
+ `ghagga-forge` is GHAGGA's forge-agnostic port layer: the canonical, provider-neutral domain types and abstract ports (forge adapter, CI runner, credential provider, webhook codec, adapter registry) that the core review engine and the server talk to instead of any concrete forge SDK. It keeps GHAGGA decoupled from GitHub, GitLab, or Gitea specifics so the same review pipeline can target any forge. The package depends on `ghagga-core` in TYPE position only, and `ghagga-core` must never depend on it (R-AGNOSTIC).
@@ -0,0 +1,102 @@
1
+ /**
2
+ * GitHubAppCredentialProvider — the REAL P2 {@link ForgeCredentialProvider} for
3
+ * the GitHub App installation-token flow (SDD forge-agnostic task 2.1).
4
+ *
5
+ * Replaces P1's `TemporaryGitHubTokenSource` (which minted a fresh token on every
6
+ * `getToken()` call, no cache). This provider delivers the R-TOKEN contract:
7
+ *
8
+ * - TTL CACHE: a minted token is reused until `expiresAtMs - SKEW_SECONDS`.
9
+ * - BUDGET VALIDITY: `getToken()` returns a token valid for >= `BUDGET_SECONDS`
10
+ * of REMAINING life. If the cached token would expire within that budget, it
11
+ * is re-minted PROACTIVELY (never "wait for a 401"). This is what makes the
12
+ * ~11-min fetch+dispatch+poll phase reuse one token AND guarantees the
13
+ * postback token outlives the postback.
14
+ * - SINGLEFLIGHT: concurrent `getToken()` calls with no valid cached token
15
+ * share ONE in-flight mint promise (no duplicate concurrent mints).
16
+ * - 401/403 FORCE-REFRESH: {@link GitHubAppCredentialProvider.invalidate}
17
+ * drops the cache so the next `getToken()` re-mints. The caller signals an
18
+ * auth failure (401/403) by calling `invalidate()`.
19
+ * - HARD-FAIL: if the mint POST fails HARD (non-401, e.g. 500/network), the
20
+ * in-flight promise rejects and `getToken()` REJECTS — NO stale/empty token
21
+ * is returned, so the job fails fast.
22
+ *
23
+ * DEPENDENCY INVERSION (R-AGNOSTIC): like the adapter, this class does NOT import
24
+ * `apps/server`. The real expiry-carrying mint is injected as
25
+ * {@link GitHubInstallationTokenMintWithExpiry}; the composition root in
26
+ * apps/server adapts `client.getInstallationToken` to also surface `expires_at`.
27
+ *
28
+ * DETERMINISTIC CLOCK: time is read through an injected `now()` (defaults to
29
+ * {@link Date.now}). `Date.now` is not otherwise controllable in tests; injecting
30
+ * it lets the fake-clock tests (task 2.4) drive TTL/budget/expiry decisions
31
+ * deterministically.
32
+ */
33
+ import type { ForgeCredentialProvider } from '../../ports/credential-provider.js';
34
+ import type { GitHubInstallationTokenMintWithExpiry } from './github-client-port.js';
35
+ /**
36
+ * Early-refresh margin (seconds). The cached token is treated as expired this
37
+ * many seconds BEFORE its real `expiresAtMs`, absorbing clock skew between this
38
+ * process and GitHub so a token never gets USED in the moments around its true
39
+ * expiry. (Design Open Question resolved: a fixed value is enough for P1–P4.)
40
+ */
41
+ export declare const SKEW_SECONDS = 60;
42
+ /**
43
+ * Required remaining validity (seconds). `getToken()` guarantees the returned
44
+ * token has at least this much life left; otherwise it re-mints PROACTIVELY.
45
+ * This is the "token must OUTLIVE the postback" budget — sized so a token handed
46
+ * out just before the comment postback cannot expire mid-postback.
47
+ */
48
+ export declare const BUDGET_SECONDS = 120;
49
+ /** Construction options for {@link GitHubAppCredentialProvider}. */
50
+ export interface GitHubAppCredentialProviderDeps {
51
+ /**
52
+ * Injected expiry-carrying installation-token mint. Real impl = the apps/server
53
+ * composition root adapting `client.getInstallationToken` to surface
54
+ * `expires_at`. MUST reject on a HARD failure (non-401) — the provider relies
55
+ * on that rejection to fail fast.
56
+ */
57
+ mint: GitHubInstallationTokenMintWithExpiry;
58
+ /** GitHub App installation id. */
59
+ installationId: number;
60
+ /** GitHub App id. */
61
+ appId: string;
62
+ /** GitHub App private key (PEM). */
63
+ privateKey: string;
64
+ /** Optional repository-id scoping for the minted token. */
65
+ repositoryIds?: number[];
66
+ /**
67
+ * Injected clock — epoch millis. Defaults to {@link Date.now}. Override in
68
+ * tests to make TTL/budget/expiry decisions deterministic.
69
+ */
70
+ now?: () => number;
71
+ }
72
+ /**
73
+ * TTL-cached, single-flight, budget-valid GitHub installation-token provider.
74
+ *
75
+ * @see {@link ForgeCredentialProvider} — the port it implements.
76
+ */
77
+ export declare class GitHubAppCredentialProvider implements ForgeCredentialProvider {
78
+ #private;
79
+ constructor(deps: GitHubAppCredentialProviderDeps);
80
+ /**
81
+ * Resolve a budget-valid installation token.
82
+ *
83
+ * Returns the cached token when it still has >= `BUDGET_SECONDS` of life
84
+ * (minus `SKEW_SECONDS` margin). Otherwise mints a fresh one — coalescing
85
+ * concurrent callers onto a single in-flight mint. Rejects (no stale token) if
86
+ * the mint fails hard.
87
+ */
88
+ getToken(): Promise<string>;
89
+ /**
90
+ * Drop the cached token so the NEXT `getToken()` re-mints. The caller invokes
91
+ * this on a 401/403 auth failure (the token was revoked/rotated server-side
92
+ * before its advertised expiry).
93
+ *
94
+ * FIX 4 (in-flight fence): a mint that was ALREADY in flight when this is
95
+ * called will still RESOLVE (its awaiter gets that token), but it will NOT be
96
+ * re-cached — it belongs to a pre-invalidation generation and could be the very
97
+ * token that was just revoked. The next fresh `getToken()` therefore mints
98
+ * anew rather than reusing a possibly-revoked in-flight result.
99
+ */
100
+ invalidate(): void;
101
+ }
102
+ //# sourceMappingURL=github-app-credential-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-app-credential-provider.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/github-app-credential-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,KAAK,EACV,qCAAqC,EAEtC,MAAM,yBAAyB,CAAC;AAEjC;;;;;GAKG;AACH,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;;;;GAKG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC,oEAAoE;AACpE,MAAM,WAAW,+BAA+B;IAC9C;;;;;OAKG;IACH,IAAI,EAAE,qCAAqC,CAAC;IAC5C,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,qBAAa,2BAA4B,YAAW,uBAAuB;;gBA4B7D,IAAI,EAAE,+BAA+B;IASjD;;;;;;;OAOG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAoDjC;;;;;;;;;;OAUG;IACH,UAAU,IAAI,IAAI;CAoBnB"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * GitHubAppCredentialProvider — the REAL P2 {@link ForgeCredentialProvider} for
3
+ * the GitHub App installation-token flow (SDD forge-agnostic task 2.1).
4
+ *
5
+ * Replaces P1's `TemporaryGitHubTokenSource` (which minted a fresh token on every
6
+ * `getToken()` call, no cache). This provider delivers the R-TOKEN contract:
7
+ *
8
+ * - TTL CACHE: a minted token is reused until `expiresAtMs - SKEW_SECONDS`.
9
+ * - BUDGET VALIDITY: `getToken()` returns a token valid for >= `BUDGET_SECONDS`
10
+ * of REMAINING life. If the cached token would expire within that budget, it
11
+ * is re-minted PROACTIVELY (never "wait for a 401"). This is what makes the
12
+ * ~11-min fetch+dispatch+poll phase reuse one token AND guarantees the
13
+ * postback token outlives the postback.
14
+ * - SINGLEFLIGHT: concurrent `getToken()` calls with no valid cached token
15
+ * share ONE in-flight mint promise (no duplicate concurrent mints).
16
+ * - 401/403 FORCE-REFRESH: {@link GitHubAppCredentialProvider.invalidate}
17
+ * drops the cache so the next `getToken()` re-mints. The caller signals an
18
+ * auth failure (401/403) by calling `invalidate()`.
19
+ * - HARD-FAIL: if the mint POST fails HARD (non-401, e.g. 500/network), the
20
+ * in-flight promise rejects and `getToken()` REJECTS — NO stale/empty token
21
+ * is returned, so the job fails fast.
22
+ *
23
+ * DEPENDENCY INVERSION (R-AGNOSTIC): like the adapter, this class does NOT import
24
+ * `apps/server`. The real expiry-carrying mint is injected as
25
+ * {@link GitHubInstallationTokenMintWithExpiry}; the composition root in
26
+ * apps/server adapts `client.getInstallationToken` to also surface `expires_at`.
27
+ *
28
+ * DETERMINISTIC CLOCK: time is read through an injected `now()` (defaults to
29
+ * {@link Date.now}). `Date.now` is not otherwise controllable in tests; injecting
30
+ * it lets the fake-clock tests (task 2.4) drive TTL/budget/expiry decisions
31
+ * deterministically.
32
+ */
33
+ /**
34
+ * Early-refresh margin (seconds). The cached token is treated as expired this
35
+ * many seconds BEFORE its real `expiresAtMs`, absorbing clock skew between this
36
+ * process and GitHub so a token never gets USED in the moments around its true
37
+ * expiry. (Design Open Question resolved: a fixed value is enough for P1–P4.)
38
+ */
39
+ export const SKEW_SECONDS = 60;
40
+ /**
41
+ * Required remaining validity (seconds). `getToken()` guarantees the returned
42
+ * token has at least this much life left; otherwise it re-mints PROACTIVELY.
43
+ * This is the "token must OUTLIVE the postback" budget — sized so a token handed
44
+ * out just before the comment postback cannot expire mid-postback.
45
+ */
46
+ export const BUDGET_SECONDS = 120;
47
+ /**
48
+ * TTL-cached, single-flight, budget-valid GitHub installation-token provider.
49
+ *
50
+ * @see {@link ForgeCredentialProvider} — the port it implements.
51
+ */
52
+ export class GitHubAppCredentialProvider {
53
+ #mint;
54
+ #installationId;
55
+ #appId;
56
+ #privateKey;
57
+ #repositoryIds;
58
+ #now;
59
+ /** The last successfully minted token + its expiry, or null when uncached. */
60
+ #cached = null;
61
+ /**
62
+ * The in-flight mint promise, or null when no mint is running. SINGLEFLIGHT:
63
+ * concurrent callers that find no valid cached token await THIS shared promise
64
+ * instead of each starting their own mint.
65
+ */
66
+ #inFlight = null;
67
+ /**
68
+ * Monotonic generation counter, bumped on every {@link invalidate}. FIX 4
69
+ * (in-flight fence): a mint that STARTED before an `invalidate()` must NOT
70
+ * repopulate the cache when it resolves afterwards — its token belongs to a
71
+ * pre-invalidation generation (it may be the very token that just got revoked).
72
+ * Each `getToken` snapshots the generation at mint-start; on resolution it
73
+ * caches ONLY if the snapshot still matches the current generation.
74
+ */
75
+ #generation = 0;
76
+ constructor(deps) {
77
+ this.#mint = deps.mint;
78
+ this.#installationId = deps.installationId;
79
+ this.#appId = deps.appId;
80
+ this.#privateKey = deps.privateKey;
81
+ this.#repositoryIds = deps.repositoryIds;
82
+ this.#now = deps.now ?? Date.now;
83
+ }
84
+ /**
85
+ * Resolve a budget-valid installation token.
86
+ *
87
+ * Returns the cached token when it still has >= `BUDGET_SECONDS` of life
88
+ * (minus `SKEW_SECONDS` margin). Otherwise mints a fresh one — coalescing
89
+ * concurrent callers onto a single in-flight mint. Rejects (no stale token) if
90
+ * the mint fails hard.
91
+ */
92
+ async getToken() {
93
+ const cached = this.#cached;
94
+ if (cached !== null && this.#isUsable(cached)) {
95
+ return cached.token;
96
+ }
97
+ // SINGLEFLIGHT: if a mint is already running, await it instead of starting
98
+ // a second one. All concurrent callers share the same promise + result.
99
+ if (this.#inFlight !== null) {
100
+ const minted = await this.#inFlight;
101
+ return minted.token;
102
+ }
103
+ // FIX 4: snapshot the generation at mint-START. If invalidate() bumps the
104
+ // generation while this mint is in flight, the resolution below will see a
105
+ // mismatch and DISCARD the result (a token from a pre-invalidation generation
106
+ // must not be re-cached). The token is still RETURNED to this caller (it is a
107
+ // fresh acquisition for them); it just isn't promoted to the shared cache.
108
+ const startGeneration = this.#generation;
109
+ const inFlight = this.#mint(this.#installationId, this.#appId, this.#privateKey, this.#repositoryIds && this.#repositoryIds.length > 0
110
+ ? { repositoryIds: this.#repositoryIds }
111
+ : undefined);
112
+ this.#inFlight = inFlight;
113
+ try {
114
+ const minted = await inFlight;
115
+ // HARD-FAIL safety: only cache on success. On rejection we fall through to
116
+ // the finally + the throw below — the cache is NEVER populated with a
117
+ // stale/empty token, so the job fails fast.
118
+ //
119
+ // FIX 4 fence: only cache if no invalidate() happened during this mint.
120
+ // Otherwise discard (do not re-cache) — but still return the token to the
121
+ // caller who awaited this specific mint.
122
+ if (this.#generation === startGeneration) {
123
+ this.#cached = minted;
124
+ }
125
+ return minted.token;
126
+ }
127
+ finally {
128
+ // Clear the in-flight slot whether the mint resolved or rejected, so a
129
+ // failed mint does not wedge every future caller onto a rejected promise.
130
+ if (this.#inFlight === inFlight) {
131
+ this.#inFlight = null;
132
+ }
133
+ }
134
+ }
135
+ /**
136
+ * Drop the cached token so the NEXT `getToken()` re-mints. The caller invokes
137
+ * this on a 401/403 auth failure (the token was revoked/rotated server-side
138
+ * before its advertised expiry).
139
+ *
140
+ * FIX 4 (in-flight fence): a mint that was ALREADY in flight when this is
141
+ * called will still RESOLVE (its awaiter gets that token), but it will NOT be
142
+ * re-cached — it belongs to a pre-invalidation generation and could be the very
143
+ * token that was just revoked. The next fresh `getToken()` therefore mints
144
+ * anew rather than reusing a possibly-revoked in-flight result.
145
+ */
146
+ invalidate() {
147
+ this.#cached = null;
148
+ // FIX 4: bump the generation so any mint already IN FLIGHT (started before
149
+ // this call) will NOT repopulate the cache when it resolves — its token
150
+ // belongs to a now-superseded generation (possibly the just-revoked token).
151
+ this.#generation += 1;
152
+ }
153
+ /**
154
+ * A cached token is usable when its remaining life (with the skew margin
155
+ * applied) still covers the required budget. i.e.
156
+ * expiresAtMs - SKEW*1000 >= now + BUDGET*1000
157
+ * Equivalently: the skew-adjusted expiry is at least `BUDGET_SECONDS` in the
158
+ * future. Otherwise it must be re-minted PROACTIVELY (not via a 401).
159
+ */
160
+ #isUsable(token) {
161
+ const skewAdjustedExpiry = token.expiresAtMs - SKEW_SECONDS * 1000;
162
+ const budgetDeadline = this.#now() + BUDGET_SECONDS * 1000;
163
+ return skewAdjustedExpiry >= budgetDeadline;
164
+ }
165
+ }
166
+ //# sourceMappingURL=github-app-credential-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-app-credential-provider.js","sourceRoot":"","sources":["../../../src/adapters/github/github-app-credential-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * GitHub client PORT (dependency inversion seam for the GitHub adapter).
3
+ *
4
+ * R-AGNOSTIC / boundary rule: `packages/forge` MUST NOT import `apps/server`
5
+ * (the boundary lint forbids it). The concrete GitHub HTTP client lives in
6
+ * `apps/server/src/github/client.ts`. To wrap it WITHOUT importing it, the
7
+ * adapter declares — here — the exact function-set it depends on, and the caller
8
+ * (review.ts, task 1.4) injects the real `client.ts` functions at construction.
9
+ *
10
+ * This interface mirrors the SIGNATURES of the client.ts functions the adapter
11
+ * folds over (owner/repo/token positional style). It is intentionally
12
+ * GitHub-native (numeric ids, GitHub-shaped returns): the adapter is the layer
13
+ * that maps GitHub-native shapes ↔ canonical forge types. Comment ids cross THIS
14
+ * seam as PLAIN numbers (GitHub-native) — boxing into `CommentId` happens
15
+ * review.ts-LOCAL in 1.4, not here.
16
+ *
17
+ * Graph return types are kept structural (`unknown`-validated downstream is done
18
+ * inside client.ts itself) so this port does NOT need a value/type import of
19
+ * core; client.ts already returns the validated `DependencyGraph | null` /
20
+ * `GraphMetadata | null`. We re-state those as type-only imports from core,
21
+ * which the boundary lint explicitly permits (`import type` from core).
22
+ */
23
+ import type { DependencyGraph, GraphMetadata } from 'ghagga-core';
24
+ /** Reaction emojis the underlying GitHub client accepts. */
25
+ export type GitHubReactionContent = '+1' | '-1' | 'laugh' | 'confused' | 'heart' | 'hooray' | 'rocket' | 'eyes';
26
+ /**
27
+ * The set of GitHub client functions the {@link GitHubForgeAdapter} depends on.
28
+ *
29
+ * Each member matches the corresponding `apps/server/src/github/client.ts`
30
+ * export by signature. The adapter receives an implementation of this port via
31
+ * its constructor (dependency inversion) so the forge package never imports the
32
+ * server.
33
+ */
34
+ export interface GitHubClientPort {
35
+ /** Fetch the unified diff text for a PR. */
36
+ fetchPRDiff(owner: string, repo: string, prNumber: number, token: string): Promise<string>;
37
+ /** Fetch PR details (head SHA, base branch, author login). */
38
+ fetchPRDetails(owner: string, repo: string, prNumber: number, token: string): Promise<{
39
+ headSha: string;
40
+ baseBranch: string;
41
+ prAuthor: string;
42
+ }>;
43
+ /** Fetch the list of changed file paths for a PR. */
44
+ getPRFileList(owner: string, repo: string, prNumber: number, token: string): Promise<string[]>;
45
+ /** Fetch commit messages for a PR. */
46
+ getPRCommitMessages(owner: string, repo: string, prNumber: number, token: string): Promise<string[]>;
47
+ /** Post a fresh comment; returns the GitHub-native numeric id. */
48
+ postComment(owner: string, repo: string, prNumber: number, body: string, token: string): Promise<{
49
+ id: number;
50
+ }>;
51
+ /** Find existing GHAGGA comments; returns latest + stale numeric ids, or null. */
52
+ findExistingComment(owner: string, repo: string, prNumber: number, token: string): Promise<{
53
+ latestId: number;
54
+ staleIds: number[];
55
+ } | null>;
56
+ /** Delete a comment by its GitHub-native numeric id. */
57
+ deleteComment(owner: string, repo: string, commentId: number, token: string): Promise<void>;
58
+ /** Update a comment in place by its GitHub-native numeric id. */
59
+ updateComment(owner: string, repo: string, commentId: number, body: string, token: string): Promise<void>;
60
+ /** Add a reaction to an issue comment (best-effort in client.ts). */
61
+ addCommentReaction(owner: string, repo: string, commentId: number, reaction: GitHubReactionContent, token: string): Promise<void>;
62
+ /** Read the dependency graph from the `ghagga/graph` orphan branch, or null. */
63
+ fetchGraphFromBranch(owner: string, repo: string, token: string): Promise<DependencyGraph | null>;
64
+ /** Read graph metadata from the `ghagga/graph` orphan branch, or null. */
65
+ fetchGraphMetadata(owner: string, repo: string, token: string): Promise<GraphMetadata | null>;
66
+ }
67
+ /**
68
+ * A minted installation token plus its absolute expiry.
69
+ *
70
+ * `expiresAtMs` is the token's expiry as an epoch-millis timestamp (comparable to
71
+ * `Date.now()`), derived by the composition root from the GitHub access-token
72
+ * response `expires_at` (an ISO-8601 string the REST API already returns).
73
+ */
74
+ export interface MintedInstallationToken {
75
+ /** The installation access token string. */
76
+ token: string;
77
+ /** Absolute expiry, epoch millis (comparable to {@link Date.now}). */
78
+ expiresAtMs: number;
79
+ }
80
+ /**
81
+ * Mints a GitHub installation access token AND reports its expiry (P2).
82
+ *
83
+ * This is the expiry-carrying mint the {@link GitHubAppCredentialProvider} TTL
84
+ * cache requires. The underlying `client.getInstallationToken` returns ONLY the
85
+ * token string today (it discards the GitHub `expires_at`); the composition root
86
+ * (apps/server) adapts it to ALSO surface the expiry so the forge package can
87
+ * cache without importing apps/server (dependency inversion, R-AGNOSTIC).
88
+ */
89
+ export type GitHubInstallationTokenMintWithExpiry = (installationId: number, appId: string, privateKey: string, options?: {
90
+ repositoryIds?: number[];
91
+ }) => Promise<MintedInstallationToken>;
92
+ //# sourceMappingURL=github-client-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-client-port.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/github-client-port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * GitHub client PORT (dependency inversion seam for the GitHub adapter).
3
+ *
4
+ * R-AGNOSTIC / boundary rule: `packages/forge` MUST NOT import `apps/server`
5
+ * (the boundary lint forbids it). The concrete GitHub HTTP client lives in
6
+ * `apps/server/src/github/client.ts`. To wrap it WITHOUT importing it, the
7
+ * adapter declares — here — the exact function-set it depends on, and the caller
8
+ * (review.ts, task 1.4) injects the real `client.ts` functions at construction.
9
+ *
10
+ * This interface mirrors the SIGNATURES of the client.ts functions the adapter
11
+ * folds over (owner/repo/token positional style). It is intentionally
12
+ * GitHub-native (numeric ids, GitHub-shaped returns): the adapter is the layer
13
+ * that maps GitHub-native shapes ↔ canonical forge types. Comment ids cross THIS
14
+ * seam as PLAIN numbers (GitHub-native) — boxing into `CommentId` happens
15
+ * review.ts-LOCAL in 1.4, not here.
16
+ *
17
+ * Graph return types are kept structural (`unknown`-validated downstream is done
18
+ * inside client.ts itself) so this port does NOT need a value/type import of
19
+ * core; client.ts already returns the validated `DependencyGraph | null` /
20
+ * `GraphMetadata | null`. We re-state those as type-only imports from core,
21
+ * which the boundary lint explicitly permits (`import type` from core).
22
+ */
23
+ export {};
24
+ //# sourceMappingURL=github-client-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-client-port.js","sourceRoot":"","sources":["../../../src/adapters/github/github-client-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * GitHubForgeAdapter — wraps the GitHub HTTP client behind the forge-agnostic
3
+ * {@link ForgeAdapterBase} + {@link ReactionCapable} + {@link GraphReadCapable}
4
+ * surfaces.
5
+ *
6
+ * DEPENDENCY INVERSION (boundary rule R-AGNOSTIC):
7
+ * `packages/forge` MUST NOT import `apps/server`. The concrete GitHub client
8
+ * lives in `apps/server/src/github/client.ts`. So this adapter NEVER imports
9
+ * that client directly — it depends on the injected {@link GitHubClientPort}
10
+ * (declared inside the forge package). Task 1.4 (review.ts) constructs the
11
+ * adapter passing the real `client.ts` functions as the port implementation:
12
+ *
13
+ * new GitHubForgeAdapter({ client, token, owner, repo })
14
+ *
15
+ * CAPABILITIES: reactions ✅, graphRead ✅ (both methods co-present),
16
+ * inlineComments ❌ (GitHub inline review is deferred — no `publishInline`).
17
+ * The `capabilities` field is a HINT only (R-CAPABILITY): callers guard optional
18
+ * methods by method-presence, never by these flags.
19
+ *
20
+ * COMMENT IDs: this adapter returns PLAIN GitHub-native numeric ids (number /
21
+ * number[]). Boxing into {@link CommentId} happens caller-LOCAL in review.ts.
22
+ * The adapter accepts a boxed {@link CommentId} in `addReaction` (port contract)
23
+ * and unwraps `raw` to the GitHub-native number internally.
24
+ */
25
+ import type { DependencyGraph, GraphMetadata } from 'ghagga-core';
26
+ import type { ForgeAdapterBase, GraphReadCapable, ReactionCapable, ReactionKind } from '../../ports/forge-adapter.js';
27
+ import { REACTION_KIND } from '../../ports/forge-adapter.js';
28
+ import type { ChangedFile, ChangeRequest, ChangeRequestRef, CommentId, CommentMarker, Commit, ForgeCapabilities, RepoRef, UnifiedDiff, UpsertSummaryResult } from '../../types.js';
29
+ import type { GitHubClientPort } from './github-client-port.js';
30
+ /** Construction options for {@link GitHubForgeAdapter}. */
31
+ export interface GitHubForgeAdapterDeps {
32
+ /** Injected GitHub client function-set (real impl = apps/server client.ts). */
33
+ client: GitHubClientPort;
34
+ /** Installation access token used for all calls. */
35
+ token: string;
36
+ /** Repository owner (login). */
37
+ owner: string;
38
+ /** Repository name. */
39
+ repo: string;
40
+ }
41
+ /**
42
+ * GitHub implementation of the forge adapter.
43
+ *
44
+ * Implements the mandatory base plus the reaction and graph-read capabilities.
45
+ * Deliberately does NOT implement {@link InlineCapable} (GitHub inline review
46
+ * deferred), so `publishInline` is ABSENT — callers guarding by method-presence
47
+ * will skip inline publishing cleanly.
48
+ */
49
+ export declare class GitHubForgeAdapter implements ForgeAdapterBase, ReactionCapable, GraphReadCapable {
50
+ #private;
51
+ readonly capabilities: ForgeCapabilities;
52
+ constructor(deps: GitHubForgeAdapterDeps);
53
+ fetchDiff(ref: ChangeRequestRef): Promise<UnifiedDiff>;
54
+ fetchChangeRequest(ref: ChangeRequestRef): Promise<ChangeRequest>;
55
+ fetchFileList(ref: ChangeRequestRef): Promise<ChangedFile[]>;
56
+ fetchCommits(ref: ChangeRequestRef): Promise<Commit[]>;
57
+ /**
58
+ * Idempotently upsert the single GHAGGA summary comment.
59
+ *
60
+ * Behavior (preserves the review.ts baseline exactly):
61
+ * 1. find existing GHAGGA comments (latest + stale).
62
+ * 2. delete ALL of them in the order `[latestId, ...staleIds]` — BEST-EFFORT:
63
+ * each delete is wrapped in try/catch so BOTH 404 (already gone) AND any
64
+ * non-404 failure are tolerated and do NOT block the repost. Only ids that
65
+ * actually deleted (no throw) are reported in `deleted`.
66
+ * 3. post a FRESH comment at the bottom — this is the ONLY failure that
67
+ * propagates (a failed post means no summary exists, which is fatal).
68
+ *
69
+ * Returns GitHub-native numeric ids (boxing happens caller-local).
70
+ *
71
+ * NOTE: `marker` is accepted for the port contract but is NOT threaded into
72
+ * the client. The GitHub adapter currently matches the FIXED
73
+ * `REVIEW_COMMENT_MARKER` (`<!-- ghagga-review -->`) hard-coded inside
74
+ * client.findExistingComment (a "stale ghagga comment" = any comment whose body
75
+ * contains that marker; author/bot status is NOT inspected). This is
76
+ * baseline-faithful: threading `marker` into
77
+ * GitHubClientPort.findExistingComment is DEFERRED (would change the port
78
+ * signature). Consequently, if review.ts (1.4) passes a `marker`, it MUST equal
79
+ * the existing `REVIEW_COMMENT_MARKER` — any other value is silently ignored.
80
+ */
81
+ upsertSummaryComment(ref: ChangeRequestRef, body: string, _marker: CommentMarker): Promise<UpsertSummaryResult>;
82
+ /**
83
+ * Add a reaction to a comment (R-5).
84
+ *
85
+ * CRITICAL: the trigger-comment reaction MUST live in the adapter — it was
86
+ * accidentally dropped once before. It is preserved here, wrapping
87
+ * client.addCommentReaction (which is itself best-effort / non-throwing).
88
+ */
89
+ addReaction(commentId: CommentId, reaction: ReactionKind): Promise<void>;
90
+ /**
91
+ * Read the dependency graph from the `ghagga/graph` orphan branch.
92
+ *
93
+ * The orphan-branch ref (`?ref=ghagga/graph`), the 404→null behavior, and the
94
+ * typed JSON validation all live INSIDE client.fetchGraphFromBranch — this
95
+ * adapter preserves them by delegating, returning `null` for "no graph".
96
+ */
97
+ fetchGraph(_repo: RepoRef): Promise<DependencyGraph | null>;
98
+ /**
99
+ * Read graph metadata from the `ghagga/graph` orphan branch (orphan-ref +
100
+ * 404→null + validation handled inside client.fetchGraphMetadata).
101
+ */
102
+ fetchGraphMetadata(_repo: RepoRef): Promise<GraphMetadata | null>;
103
+ }
104
+ export { REACTION_KIND };
105
+ //# sourceMappingURL=github-forge-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-forge-adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/github-forge-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAElE,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,MAAM,EACN,iBAAiB,EACjB,OAAO,EACP,WAAW,EACX,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,gBAAgB,EAAyB,MAAM,yBAAyB,CAAC;AAEvF,2DAA2D;AAC3D,MAAM,WAAW,sBAAsB;IACrC,+EAA+E;IAC/E,MAAM,EAAE,gBAAgB,CAAC;IACzB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AA4BD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,gBAAgB,EAAE,eAAe,EAAE,gBAAgB;;IAC5F,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAItC;gBAOU,IAAI,EAAE,sBAAsB;IAqClC,SAAS,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAOtD,kBAAkB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;IAcjE,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAY5D,YAAY,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAa5D;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,oBAAoB,CACxB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,mBAAmB,CAAC;IAiD/B;;;;;;OAMG;IACG,WAAW,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAc9E;;;;;;OAMG;IACG,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAIjE;;;OAGG;IACG,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;CAGxE;AAGD,OAAO,EAAE,aAAa,EAAE,CAAC"}