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.
- package/LICENSE +21 -0
- package/README.md +3 -0
- package/dist/adapters/github/github-app-credential-provider.d.ts +102 -0
- package/dist/adapters/github/github-app-credential-provider.d.ts.map +1 -0
- package/dist/adapters/github/github-app-credential-provider.js +166 -0
- package/dist/adapters/github/github-app-credential-provider.js.map +1 -0
- package/dist/adapters/github/github-client-port.d.ts +92 -0
- package/dist/adapters/github/github-client-port.d.ts.map +1 -0
- package/dist/adapters/github/github-client-port.js +24 -0
- package/dist/adapters/github/github-client-port.js.map +1 -0
- package/dist/adapters/github/github-forge-adapter.d.ts +105 -0
- package/dist/adapters/github/github-forge-adapter.d.ts.map +1 -0
- package/dist/adapters/github/github-forge-adapter.js +225 -0
- package/dist/adapters/github/github-forge-adapter.js.map +1 -0
- package/dist/adapters/github/static-token-provider.d.ts +30 -0
- package/dist/adapters/github/static-token-provider.d.ts.map +1 -0
- package/dist/adapters/github/static-token-provider.js +35 -0
- package/dist/adapters/github/static-token-provider.js.map +1 -0
- package/dist/adapters/gitlab/gitlab-client-port.d.ts +82 -0
- package/dist/adapters/gitlab/gitlab-client-port.d.ts.map +1 -0
- package/dist/adapters/gitlab/gitlab-client-port.js +27 -0
- package/dist/adapters/gitlab/gitlab-client-port.js.map +1 -0
- package/dist/adapters/gitlab/gitlab-forge-adapter.d.ts +118 -0
- package/dist/adapters/gitlab/gitlab-forge-adapter.d.ts.map +1 -0
- package/dist/adapters/gitlab/gitlab-forge-adapter.js +238 -0
- package/dist/adapters/gitlab/gitlab-forge-adapter.js.map +1 -0
- package/dist/comment-id.d.ts +45 -0
- package/dist/comment-id.d.ts.map +1 -0
- package/dist/comment-id.js +48 -0
- package/dist/comment-id.js.map +1 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +67 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/lint-boundary.d.ts +36 -0
- package/dist/lint-boundary.d.ts.map +1 -0
- package/dist/lint-boundary.impl.d.mts +41 -0
- package/dist/lint-boundary.impl.mjs +400 -0
- package/dist/lint-boundary.js +35 -0
- package/dist/lint-boundary.js.map +1 -0
- package/dist/ports/ci-runner.d.ts +48 -0
- package/dist/ports/ci-runner.d.ts.map +1 -0
- package/dist/ports/ci-runner.js +10 -0
- package/dist/ports/ci-runner.js.map +1 -0
- package/dist/ports/credential-provider.d.ts +32 -0
- package/dist/ports/credential-provider.d.ts.map +1 -0
- package/dist/ports/credential-provider.js +10 -0
- package/dist/ports/credential-provider.js.map +1 -0
- package/dist/ports/forge-adapter.d.ts +174 -0
- package/dist/ports/forge-adapter.d.ts.map +1 -0
- package/dist/ports/forge-adapter.js +34 -0
- package/dist/ports/forge-adapter.js.map +1 -0
- package/dist/ports/webhook-codec.d.ts +41 -0
- package/dist/ports/webhook-codec.d.ts.map +1 -0
- package/dist/ports/webhook-codec.js +18 -0
- package/dist/ports/webhook-codec.js.map +1 -0
- package/dist/project.d.ts +32 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +41 -0
- package/dist/project.js.map +1 -0
- package/dist/ref.d.ts +20 -0
- package/dist/ref.d.ts.map +1 -0
- package/dist/ref.js +21 -0
- package/dist/ref.js.map +1 -0
- package/dist/registry.d.ts +69 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +68 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +310 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +50 -0
- package/dist/types.js.map +1 -0
- 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"}
|