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
@@ -0,0 +1,225 @@
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 { ForgeAuthError, getErrorStatus } from '../../errors.js';
26
+ import { REACTION_KIND } from '../../ports/forge-adapter.js';
27
+ import { ACTOR_KIND } from '../../types.js';
28
+ /** Maps a canonical {@link ReactionKind} to the GitHub reaction content string. */
29
+ function toGitHubReaction(reaction) {
30
+ // ReactionKind values are already GitHub-native strings ('+1','-1','eyes',
31
+ // 'rocket','confused'); this cast is the single sanctioned bridge point.
32
+ return reaction;
33
+ }
34
+ /**
35
+ * Extract the GitHub-native numeric comment id from a boxed {@link CommentId}.
36
+ * `raw` is `string | number`; GitHub comment ids are numeric.
37
+ *
38
+ * Guards against a malformed/non-GitHub id silently coercing to `NaN` (which
39
+ * would produce a `…/comments/NaN` URL). Throws a clear {@link TypeError} unless
40
+ * the result is a safe integer.
41
+ */
42
+ function toNativeCommentId(commentId) {
43
+ const { raw } = commentId;
44
+ const native = typeof raw === 'number' ? raw : Number(raw);
45
+ if (!Number.isSafeInteger(native)) {
46
+ throw new TypeError(`GitHubForgeAdapter: comment id is not a safe integer (kind=${commentId.kind}, raw=${String(raw)})`);
47
+ }
48
+ return native;
49
+ }
50
+ /**
51
+ * GitHub implementation of the forge adapter.
52
+ *
53
+ * Implements the mandatory base plus the reaction and graph-read capabilities.
54
+ * Deliberately does NOT implement {@link InlineCapable} (GitHub inline review
55
+ * deferred), so `publishInline` is ABSENT — callers guarding by method-presence
56
+ * will skip inline publishing cleanly.
57
+ */
58
+ export class GitHubForgeAdapter {
59
+ capabilities = {
60
+ reactions: true,
61
+ inlineComments: false,
62
+ graphRead: true,
63
+ };
64
+ #client;
65
+ #token;
66
+ #owner;
67
+ #repo;
68
+ constructor(deps) {
69
+ this.#client = deps.client;
70
+ this.#token = deps.token;
71
+ this.#owner = deps.owner;
72
+ this.#repo = deps.repo;
73
+ }
74
+ /**
75
+ * Run a client call, reclassifying a 401/403 failure as a {@link ForgeAuthError}.
76
+ *
77
+ * This is the SINGLE point where a GitHub auth failure (the token was rejected
78
+ * server-side) becomes the typed signal the worker catches to drive the in-job
79
+ * re-mint + retry (P2 401-recovery). NON-auth failures are rethrown UNCHANGED
80
+ * (not reclassified) so retry logic only fires for genuine auth failures.
81
+ *
82
+ * The status is read off the thrown error's `status` field (the GitHub client
83
+ * tags its errors with `GitHubApiError.status`). Errors with no usable status
84
+ * pass through untouched.
85
+ */
86
+ async #mapAuth(call) {
87
+ try {
88
+ return await call();
89
+ }
90
+ catch (error) {
91
+ const status = getErrorStatus(error);
92
+ if (status === 401 || status === 403) {
93
+ throw new ForgeAuthError(status, error instanceof Error ? error.message : `Forge auth failure (HTTP ${status})`, { cause: error });
94
+ }
95
+ throw error;
96
+ }
97
+ }
98
+ // ─── Base: reads ───────────────────────────────────────────────
99
+ async fetchDiff(ref) {
100
+ const text = await this.#mapAuth(() => this.#client.fetchPRDiff(this.#owner, this.#repo, ref.iid, this.#token));
101
+ return { text };
102
+ }
103
+ async fetchChangeRequest(ref) {
104
+ const details = await this.#mapAuth(() => this.#client.fetchPRDetails(this.#owner, this.#repo, ref.iid, this.#token));
105
+ return {
106
+ ref,
107
+ headSha: details.headSha,
108
+ baseBranch: details.baseBranch,
109
+ // GitHub PR author kind is not exposed by fetchPRDetails; default USER.
110
+ // (Bot detection, when needed, is the caller's concern via login suffix.)
111
+ author: { login: details.prAuthor, kind: ACTOR_KIND.USER },
112
+ };
113
+ }
114
+ async fetchFileList(ref) {
115
+ // client.getPRFileList returns bare paths; the GitHub PR file-list endpoint
116
+ // wrapper does NOT expose changeKind/additions/deletions, so those optional
117
+ // ChangedFile fields are OMITTED (honest absence) rather than fabricated as
118
+ // zeros. The caller flattens via project.ts (toFileList) which only consumes
119
+ // `path`.
120
+ const paths = await this.#mapAuth(() => this.#client.getPRFileList(this.#owner, this.#repo, ref.iid, this.#token));
121
+ return paths.map((path) => ({ path }));
122
+ }
123
+ async fetchCommits(ref) {
124
+ // client.getPRCommitMessages returns only messages; the GitHub PR commit-list
125
+ // endpoint wrapper does NOT expose sha/author, so those optional Commit fields
126
+ // are OMITTED (honest absence) rather than fabricated as empty strings. The
127
+ // caller flattens via toCommitMessages which only consumes `message`.
128
+ const messages = await this.#mapAuth(() => this.#client.getPRCommitMessages(this.#owner, this.#repo, ref.iid, this.#token));
129
+ return messages.map((message) => ({ message }));
130
+ }
131
+ // ─── Base: upsert summary comment (fold post/find/delete → 1) ──
132
+ /**
133
+ * Idempotently upsert the single GHAGGA summary comment.
134
+ *
135
+ * Behavior (preserves the review.ts baseline exactly):
136
+ * 1. find existing GHAGGA comments (latest + stale).
137
+ * 2. delete ALL of them in the order `[latestId, ...staleIds]` — BEST-EFFORT:
138
+ * each delete is wrapped in try/catch so BOTH 404 (already gone) AND any
139
+ * non-404 failure are tolerated and do NOT block the repost. Only ids that
140
+ * actually deleted (no throw) are reported in `deleted`.
141
+ * 3. post a FRESH comment at the bottom — this is the ONLY failure that
142
+ * propagates (a failed post means no summary exists, which is fatal).
143
+ *
144
+ * Returns GitHub-native numeric ids (boxing happens caller-local).
145
+ *
146
+ * NOTE: `marker` is accepted for the port contract but is NOT threaded into
147
+ * the client. The GitHub adapter currently matches the FIXED
148
+ * `REVIEW_COMMENT_MARKER` (`<!-- ghagga-review -->`) hard-coded inside
149
+ * client.findExistingComment (a "stale ghagga comment" = any comment whose body
150
+ * contains that marker; author/bot status is NOT inspected). This is
151
+ * baseline-faithful: threading `marker` into
152
+ * GitHubClientPort.findExistingComment is DEFERRED (would change the port
153
+ * signature). Consequently, if review.ts (1.4) passes a `marker`, it MUST equal
154
+ * the existing `REVIEW_COMMENT_MARKER` — any other value is silently ignored.
155
+ */
156
+ async upsertSummaryComment(ref, body, _marker) {
157
+ const existing = await this.#mapAuth(() => this.#client.findExistingComment(this.#owner, this.#repo, ref.iid, this.#token));
158
+ const deleted = [];
159
+ if (existing) {
160
+ // Preserve baseline delete-order: latest FIRST, then stale duplicates.
161
+ const allIds = [existing.latestId, ...existing.staleIds];
162
+ for (const commentId of allIds) {
163
+ try {
164
+ await this.#client.deleteComment(this.#owner, this.#repo, commentId, this.#token);
165
+ deleted.push(commentId);
166
+ }
167
+ catch {
168
+ // Best-effort: tolerate BOTH 404 and non-404 delete failures. A stale
169
+ // comment that cannot be deleted must NOT block the fresh repost.
170
+ //
171
+ // REALITY NOTE: the real client.ts deleteComment is fire-and-forget
172
+ // (it does NOT check response.ok and never throws), so in production
173
+ // this catch is defensive belt-and-suspenders and `deleted[]` reflects
174
+ // attempted-and-not-thrown deletes. The catch only triggers under a
175
+ // test double / future client that does throw.
176
+ }
177
+ }
178
+ }
179
+ // Always post a fresh comment at the bottom. This is the ONLY error that
180
+ // propagates — without it there would be no summary at all. A 401/403 here is
181
+ // reclassified to ForgeAuthError so the worker can re-mint + retry (P2).
182
+ const posted = await this.#mapAuth(() => this.#client.postComment(this.#owner, this.#repo, ref.iid, body, this.#token));
183
+ // Robustness guard: the real client.postComment returns `{ id }` or throws,
184
+ // so a missing id is a contract violation (e.g. a mis-shaped test double),
185
+ // never a live path. Fail loudly here rather than silently boxing
186
+ // `undefined` → the meaningless CommentId `{ kind:'github', raw:'undefined' }`
187
+ // downstream.
188
+ if (posted?.id == null) {
189
+ throw new TypeError('GitHubForgeAdapter.upsertSummaryComment: postComment returned no id (expected { id: number })');
190
+ }
191
+ return { created: posted.id, deleted };
192
+ }
193
+ // ─── ReactionCapable ───────────────────────────────────────────
194
+ /**
195
+ * Add a reaction to a comment (R-5).
196
+ *
197
+ * CRITICAL: the trigger-comment reaction MUST live in the adapter — it was
198
+ * accidentally dropped once before. It is preserved here, wrapping
199
+ * client.addCommentReaction (which is itself best-effort / non-throwing).
200
+ */
201
+ async addReaction(commentId, reaction) {
202
+ await this.#mapAuth(() => this.#client.addCommentReaction(this.#owner, this.#repo, toNativeCommentId(commentId), toGitHubReaction(reaction), this.#token));
203
+ }
204
+ // ─── GraphReadCapable (both methods co-present) ─────────────────
205
+ /**
206
+ * Read the dependency graph from the `ghagga/graph` orphan branch.
207
+ *
208
+ * The orphan-branch ref (`?ref=ghagga/graph`), the 404→null behavior, and the
209
+ * typed JSON validation all live INSIDE client.fetchGraphFromBranch — this
210
+ * adapter preserves them by delegating, returning `null` for "no graph".
211
+ */
212
+ async fetchGraph(_repo) {
213
+ return this.#client.fetchGraphFromBranch(this.#owner, this.#repo, this.#token);
214
+ }
215
+ /**
216
+ * Read graph metadata from the `ghagga/graph` orphan branch (orphan-ref +
217
+ * 404→null + validation handled inside client.fetchGraphMetadata).
218
+ */
219
+ async fetchGraphMetadata(_repo) {
220
+ return this.#client.fetchGraphMetadata(this.#owner, this.#repo, this.#token);
221
+ }
222
+ }
223
+ // Re-export so test files and 1.4 can reference the reaction-kind set succinctly.
224
+ export { REACTION_KIND };
225
+ //# sourceMappingURL=github-forge-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-forge-adapter.js","sourceRoot":"","sources":["../../../src/adapters/github/github-forge-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * StaticTokenProvider — a fixed-token {@link ForgeCredentialProvider} for the CLI
3
+ * path (SDD forge-agnostic task 2.2).
4
+ *
5
+ * Returns a long-lived token supplied up front (e.g. `GITHUB_TOKEN` / a PAT) with
6
+ * NO cache and NO refresh — there is nothing to mint. It exists so the CLI (P3)
7
+ * and GitLab-via-CLI (P4) can satisfy the SAME credential seam the server worker
8
+ * uses, making the auth strategy a pure construction-site choice (R-TOKEN: "swap
9
+ * App→Static requires no worker change").
10
+ *
11
+ * Forge-agnostic by construction: it takes the token as a plain string, so it is
12
+ * not GitHub-specific despite living under adapters/github (the only current
13
+ * consumer is the GitHub CLI path).
14
+ */
15
+ import type { ForgeCredentialProvider } from '../../ports/credential-provider.js';
16
+ /** Fixed-token credential provider (no cache, no refresh). */
17
+ export declare class StaticTokenProvider implements ForgeCredentialProvider {
18
+ #private;
19
+ constructor(token: string);
20
+ /** Return the fixed token. Always the same value; never mints or refreshes. */
21
+ getToken(): Promise<string>;
22
+ /**
23
+ * No-op: a static provider has NO cache to drop. The fixed token cannot be
24
+ * re-acquired (there is nothing to mint), so a 401/403 here is terminal — but
25
+ * the method exists to satisfy the {@link ForgeCredentialProvider} port so the
26
+ * SAME 401-recovery call-site works regardless of which provider is wired.
27
+ */
28
+ invalidate(): void;
29
+ }
30
+ //# sourceMappingURL=static-token-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-token-provider.d.ts","sourceRoot":"","sources":["../../../src/adapters/github/static-token-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAElF,8DAA8D;AAC9D,qBAAa,mBAAoB,YAAW,uBAAuB;;gBAGrD,KAAK,EAAE,MAAM;IAIzB,+EAA+E;IACzE,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAIjC;;;;;OAKG;IACH,UAAU,IAAI,IAAI;CAGnB"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * StaticTokenProvider — a fixed-token {@link ForgeCredentialProvider} for the CLI
3
+ * path (SDD forge-agnostic task 2.2).
4
+ *
5
+ * Returns a long-lived token supplied up front (e.g. `GITHUB_TOKEN` / a PAT) with
6
+ * NO cache and NO refresh — there is nothing to mint. It exists so the CLI (P3)
7
+ * and GitLab-via-CLI (P4) can satisfy the SAME credential seam the server worker
8
+ * uses, making the auth strategy a pure construction-site choice (R-TOKEN: "swap
9
+ * App→Static requires no worker change").
10
+ *
11
+ * Forge-agnostic by construction: it takes the token as a plain string, so it is
12
+ * not GitHub-specific despite living under adapters/github (the only current
13
+ * consumer is the GitHub CLI path).
14
+ */
15
+ /** Fixed-token credential provider (no cache, no refresh). */
16
+ export class StaticTokenProvider {
17
+ #token;
18
+ constructor(token) {
19
+ this.#token = token;
20
+ }
21
+ /** Return the fixed token. Always the same value; never mints or refreshes. */
22
+ async getToken() {
23
+ return this.#token;
24
+ }
25
+ /**
26
+ * No-op: a static provider has NO cache to drop. The fixed token cannot be
27
+ * re-acquired (there is nothing to mint), so a 401/403 here is terminal — but
28
+ * the method exists to satisfy the {@link ForgeCredentialProvider} port so the
29
+ * SAME 401-recovery call-site works regardless of which provider is wired.
30
+ */
31
+ invalidate() {
32
+ // intentionally empty — no cache, nothing to invalidate.
33
+ }
34
+ }
35
+ //# sourceMappingURL=static-token-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-token-provider.js","sourceRoot":"","sources":["../../../src/adapters/github/static-token-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,8DAA8D;AAC9D,MAAM,OAAO,mBAAmB;IACrB,MAAM,CAAS;IAExB,YAAY,KAAa;QACvB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,+EAA+E;IAC/E,KAAK,CAAC,QAAQ;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;;OAKG;IACH,UAAU;QACR,yDAAyD;IAC3D,CAAC;CACF"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * GitLab client PORT (dependency inversion seam for the GitLab adapter).
3
+ *
4
+ * R-AGNOSTIC / boundary rule: `packages/forge` MUST NOT import a concrete HTTP
5
+ * client. Mirroring {@link GitHubClientPort}, the {@link GitLabForgeAdapter}
6
+ * declares — here — the exact GitLab REST function-set it depends on, and the
7
+ * CALLER (the CLI, P4) injects a native-fetch implementation at construction.
8
+ *
9
+ * This interface is intentionally GitLab-native (numeric note ids, numeric
10
+ * project id, MR iid): the adapter is the layer that maps GitLab-native shapes ↔
11
+ * canonical forge types. Note ids cross THIS seam as PLAIN numbers
12
+ * (GitLab-native) — boxing into the canonical `CommentId` (kind:'gitlab') happens
13
+ * caller-LOCAL via {@link gitlabCommentId}, NOT here.
14
+ *
15
+ * SCOPE (P4 — MR summary post-back + inline-note publish):
16
+ * REAL members the adapter folds over:
17
+ * - listMrNotes (GET /projects/:id/merge_requests/:iid/notes)
18
+ * - createMrNote (POST /projects/:id/merge_requests/:iid/notes)
19
+ * - deleteMrNote (DELETE /projects/:id/merge_requests/:iid/notes/:note_id)
20
+ * - updateMrNote (PUT /projects/:id/merge_requests/:iid/notes/:note_id)
21
+ * The CLI injects real fetch-backed impls (P4); read fns the adapter does not
22
+ * need (diff/details/file-list/commits/graph) are deliberately NOT on this
23
+ * port — the GitLab v1 deliverable is the summary-comment, and the CLI sources
24
+ * the diff locally (same posture as the GitHub CLI port).
25
+ */
26
+ /** A single GitLab MR note as returned by the list endpoint (subset used). */
27
+ export interface GitLabNote {
28
+ /** GitLab-native numeric note id. */
29
+ id: number;
30
+ /** Note body (markdown) — scanned for the GHAGGA marker. */
31
+ body: string;
32
+ }
33
+ /**
34
+ * The GitLab text-diff position payload for a discussion (`position_type=text`).
35
+ *
36
+ * Mirrors the GitLab discussions API `position[...]` fields. `oldPath`/`newPath`
37
+ * are BOTH required by GitLab for a text diff note (they coincide for a
38
+ * non-renamed file). At least one of `oldLine`/`newLine` must be set.
39
+ */
40
+ export interface GitLabDiffPosition {
41
+ baseSha: string;
42
+ headSha: string;
43
+ startSha: string;
44
+ oldPath: string;
45
+ newPath: string;
46
+ oldLine?: number;
47
+ newLine?: number;
48
+ }
49
+ /**
50
+ * The set of GitLab REST functions the {@link GitLabForgeAdapter} depends on.
51
+ *
52
+ * Calls are project-id + MR-iid positional (the GitLab MR API path is
53
+ * `/projects/:id/merge_requests/:iid/...`). `projectId` is the GitLab NUMERIC
54
+ * project id (canonical — the group/project path is mutable). The adapter
55
+ * receives an implementation via its constructor (dependency inversion) so the
56
+ * forge package never imports a concrete client.
57
+ */
58
+ export interface GitLabClientPort {
59
+ /** List the notes on an MR (chronological); returns id + body per note. */
60
+ listMrNotes(projectId: string, mrIid: number, token: string): Promise<GitLabNote[]>;
61
+ /** Create a fresh MR note; returns the GitLab-native numeric note id. */
62
+ createMrNote(projectId: string, mrIid: number, body: string, token: string): Promise<{
63
+ id: number;
64
+ }>;
65
+ /** Delete an MR note by its GitLab-native numeric note id. */
66
+ deleteMrNote(projectId: string, mrIid: number, noteId: number, token: string): Promise<void>;
67
+ /** Update an MR note in place by its GitLab-native numeric note id. */
68
+ updateMrNote(projectId: string, mrIid: number, noteId: number, body: string, token: string): Promise<void>;
69
+ /**
70
+ * Create a diff-anchored MR DISCUSSION (`POST /merge_requests/:iid/discussions`
71
+ * with `position[position_type]=text`). Used by `publishInline` when the
72
+ * caller supplies a {@link GitLabDiffPosition}. Returns the GitLab-native
73
+ * numeric id of the discussion's FIRST note (so it boxes like a note id).
74
+ *
75
+ * OPTIONAL: an injected client MAY omit this (older CLI builds). When absent,
76
+ * `publishInline` degrades a positioned comment to a plain `path:line` note.
77
+ */
78
+ createMrDiscussion?(projectId: string, mrIid: number, body: string, position: GitLabDiffPosition, token: string): Promise<{
79
+ id: number;
80
+ }>;
81
+ }
82
+ //# sourceMappingURL=gitlab-client-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab-client-port.d.ts","sourceRoot":"","sources":["../../../src/adapters/gitlab/gitlab-client-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,2EAA2E;IAC3E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAEpF,yEAAyE;IACzE,YAAY,CACV,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE3B,8DAA8D;IAC9D,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7F,uEAAuE;IACvE,YAAY,CACV,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;OAQG;IACH,kBAAkB,CAAC,CACjB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5B"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * GitLab client PORT (dependency inversion seam for the GitLab adapter).
3
+ *
4
+ * R-AGNOSTIC / boundary rule: `packages/forge` MUST NOT import a concrete HTTP
5
+ * client. Mirroring {@link GitHubClientPort}, the {@link GitLabForgeAdapter}
6
+ * declares — here — the exact GitLab REST function-set it depends on, and the
7
+ * CALLER (the CLI, P4) injects a native-fetch implementation at construction.
8
+ *
9
+ * This interface is intentionally GitLab-native (numeric note ids, numeric
10
+ * project id, MR iid): the adapter is the layer that maps GitLab-native shapes ↔
11
+ * canonical forge types. Note ids cross THIS seam as PLAIN numbers
12
+ * (GitLab-native) — boxing into the canonical `CommentId` (kind:'gitlab') happens
13
+ * caller-LOCAL via {@link gitlabCommentId}, NOT here.
14
+ *
15
+ * SCOPE (P4 — MR summary post-back + inline-note publish):
16
+ * REAL members the adapter folds over:
17
+ * - listMrNotes (GET /projects/:id/merge_requests/:iid/notes)
18
+ * - createMrNote (POST /projects/:id/merge_requests/:iid/notes)
19
+ * - deleteMrNote (DELETE /projects/:id/merge_requests/:iid/notes/:note_id)
20
+ * - updateMrNote (PUT /projects/:id/merge_requests/:iid/notes/:note_id)
21
+ * The CLI injects real fetch-backed impls (P4); read fns the adapter does not
22
+ * need (diff/details/file-list/commits/graph) are deliberately NOT on this
23
+ * port — the GitLab v1 deliverable is the summary-comment, and the CLI sources
24
+ * the diff locally (same posture as the GitHub CLI port).
25
+ */
26
+ export {};
27
+ //# sourceMappingURL=gitlab-client-port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab-client-port.js","sourceRoot":"","sources":["../../../src/adapters/gitlab/gitlab-client-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * GitLabForgeAdapter — wraps a GitLab REST client behind the forge-agnostic
3
+ * {@link ForgeAdapterBase} + {@link InlineCapable} surfaces.
4
+ *
5
+ * DEPENDENCY INVERSION (boundary rule R-AGNOSTIC):
6
+ * `packages/forge` MUST NOT import a concrete HTTP client. This adapter depends
7
+ * on the injected {@link GitLabClientPort} (declared inside the forge package);
8
+ * the CLI (P4) constructs the adapter passing a native-fetch implementation:
9
+ *
10
+ * new GitLabForgeAdapter({ client, token, projectId })
11
+ *
12
+ * IDENTITY (R-GITLAB):
13
+ * - {@link RepoRef.nativeId} is the GitLab NUMERIC project id (the group/project
14
+ * `path` is mutable → the id is canonical). The adapter is constructed with the
15
+ * numeric `projectId` and routes EVERY REST call through it — never the path.
16
+ * - {@link ChangeRequestRef.iid} is the MR iid (project-scoped). The GitLab MR
17
+ * API path is `/projects/:id/merge_requests/:iid/...`.
18
+ *
19
+ * CAPABILITIES: reactions ❌ (GitLab reaction-award is not modelled — no
20
+ * `addReaction`), inlineComments ✅ (publishInline present), graphRead ❌ (no
21
+ * graph methods). The `capabilities` field is a HINT only (R-CAPABILITY): callers
22
+ * guard optional methods by method-presence, never by these flags.
23
+ *
24
+ * SCOPE — v1 deliverable is the SUMMARY COMMENT (upsertSummaryComment). The
25
+ * adapter also implements {@link InlineCapable.publishInline} to satisfy
26
+ * R-LEAK-PUBLISH: it posts N INDEPENDENT inline posts, returning a
27
+ * {@link PublishReport} where partial failure is FIRST-CLASS (never swallowed).
28
+ * INLINE POSITIONING: when an {@link InlineComment} carries `position` and the
29
+ * injected client supports `createMrDiscussion`, v1 posts a TRUE diff-anchored
30
+ * discussion (`position[position_type]=text` + the three SHAs + old/new line +
31
+ * old/new path). When `position` is absent (or the client lacks discussion
32
+ * support) it degrades to a plain MR note carrying the `path:line` prefix in the
33
+ * body. The public {@link InlineComment} type carries `position` + `oldPath`/
34
+ * `newPath` regardless, so broadening positioned coverage needs NO API change.
35
+ *
36
+ * COMMENT IDs: `upsertSummaryComment` returns PLAIN GitLab-native numeric ids
37
+ * (boxing happens caller-LOCAL via {@link gitlabCommentId}). `publishInline`
38
+ * boxes per the {@link PublishReport} contract (CommentId) since the report is
39
+ * the canonical cross-forge shape.
40
+ */
41
+ import type { ForgeAdapterBase, InlineCapable, InlineComment } from '../../ports/forge-adapter.js';
42
+ import type { ChangedFile, ChangeRequest, ChangeRequestRef, CommentMarker, Commit, ForgeCapabilities, PublishReport, UnifiedDiff, UpsertSummaryResult } from '../../types.js';
43
+ import type { GitLabClientPort } from './gitlab-client-port.js';
44
+ /** Construction options for {@link GitLabForgeAdapter}. */
45
+ export interface GitLabForgeAdapterDeps {
46
+ /** Injected GitLab REST function-set (real impl = the CLI's fetch client). */
47
+ client: GitLabClientPort;
48
+ /** GitLab Personal Access Token used for all calls. */
49
+ token: string;
50
+ /**
51
+ * The GitLab NUMERIC project id (canonical — see R-GITLAB). NOT the mutable
52
+ * group/project path. The CLI resolves this from the path via
53
+ * `GET /projects/:url-encoded-path` before constructing the adapter.
54
+ */
55
+ projectId: string;
56
+ }
57
+ /**
58
+ * GitLab implementation of the forge adapter.
59
+ *
60
+ * Implements the mandatory base (summary-comment + the read methods, the latter
61
+ * unused by the CLI v1 path and intentionally absent from the client port) plus
62
+ * {@link InlineCapable}. Deliberately does NOT implement reactions or graph read.
63
+ */
64
+ export declare class GitLabForgeAdapter implements ForgeAdapterBase, InlineCapable {
65
+ #private;
66
+ readonly capabilities: ForgeCapabilities;
67
+ constructor(deps: GitLabForgeAdapterDeps);
68
+ fetchDiff(_ref: ChangeRequestRef): Promise<UnifiedDiff>;
69
+ fetchChangeRequest(_ref: ChangeRequestRef): Promise<ChangeRequest>;
70
+ fetchFileList(_ref: ChangeRequestRef): Promise<ChangedFile[]>;
71
+ fetchCommits(_ref: ChangeRequestRef): Promise<Commit[]>;
72
+ /**
73
+ * Idempotently upsert the single GHAGGA summary note on an MR.
74
+ *
75
+ * Behavior (mirrors the GitHub adapter's delete-all-stale + repost-at-bottom):
76
+ * 1. list MR notes; match the GHAGGA-owned ones by `marker.html` substring.
77
+ * 2. delete ALL matches in `[latest, ...stale]` order — BEST-EFFORT: each
78
+ * delete is per-note try/catch so a 404 (already gone) OR any other failure
79
+ * is tolerated and does NOT block the repost. Only ids that actually deleted
80
+ * (no throw) are reported in `deleted`.
81
+ * 3. create a FRESH note at the bottom — this is the ONLY failure that
82
+ * propagates (a failed create means no summary exists, which is fatal).
83
+ *
84
+ * "latest" = the LAST marker-matching note in chronological order (GitLab list
85
+ * returns notes oldest-first), matching the GitHub adapter's convention.
86
+ *
87
+ * Returns GitLab-native numeric ids (boxing happens caller-local via
88
+ * {@link gitlabCommentId}).
89
+ */
90
+ upsertSummaryComment(ref: ChangeRequestRef, body: string, marker: CommentMarker): Promise<UpsertSummaryResult>;
91
+ /**
92
+ * Publish a batch of inline comments as N INDEPENDENT posts (R-LEAK-PUBLISH).
93
+ *
94
+ * PARTIAL FAILURE IS FIRST-CLASS: GitLab posts each comment independently, so
95
+ * each create is wrapped in its own try/catch. A failure records
96
+ * `{index, error, status?, authFailure?}` into `failed` and the loop CONTINUES
97
+ * — failures are NEVER swallowed and never abort the remaining posts. The
98
+ * `status`/`authFailure` tags let a caller tell an AUTH failure (401/403, a
99
+ * static PAT cannot recover) from a transient one without string-parsing the
100
+ * message. Successes are boxed into `CommentId` (kind:'gitlab') and collected
101
+ * into `posted`.
102
+ *
103
+ * POSITION HANDLING (v1):
104
+ * - When a comment carries `position` AND the injected client implements
105
+ * `createMrDiscussion`, the adapter posts a TRUE diff-anchored discussion
106
+ * (`position[position_type]=text` with the three SHAs + old/new line +
107
+ * old/new path — renames are honored via `oldPath`/`newPath`, defaulting to
108
+ * `path`). The boxed id is the discussion's first-note id.
109
+ * - Otherwise (no `position`, or a client without discussion support) it
110
+ * degrades to a plain MR note carrying a `path:line` body prefix so the
111
+ * anchor info is not lost. Either way the partial-failure REPORT SHAPE +
112
+ * independent-post semantics — the load-bearing R-LEAK-PUBLISH contract —
113
+ * hold. The public {@link InlineComment} type carries `position` regardless,
114
+ * so no future API change is needed to broaden positioned coverage.
115
+ */
116
+ publishInline(ref: ChangeRequestRef, comments: InlineComment[]): Promise<PublishReport>;
117
+ }
118
+ //# sourceMappingURL=gitlab-forge-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab-forge-adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/gitlab/gitlab-forge-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACnG,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,MAAM,EACN,iBAAiB,EAEjB,aAAa,EACb,WAAW,EACX,mBAAmB,EACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,2DAA2D;AAC3D,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,MAAM,EAAE,gBAAgB,CAAC;IACzB,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,qBAAa,kBAAmB,YAAW,gBAAgB,EAAE,aAAa;;IACxE,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAItC;gBAMU,IAAI,EAAE,sBAAsB;IAmCxC,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IASvD,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,aAAa,CAAC;IAMlE,aAAa,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAM7D,YAAY,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAQvD;;;;;;;;;;;;;;;;;OAiBG;IACG,oBAAoB,CACxB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,mBAAmB,CAAC;IA2C/B;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACG,aAAa,CAAC,GAAG,EAAE,gBAAgB,EAAE,QAAQ,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,aAAa,CAAC;CAqE9F"}