@simplysf/simply-gitlab-core 0.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 (59) hide show
  1. package/README.md +76 -0
  2. package/lib/auth.d.ts +9 -0
  3. package/lib/auth.js +26 -0
  4. package/lib/auth.js.map +1 -0
  5. package/lib/cicd.d.ts +29 -0
  6. package/lib/cicd.js +56 -0
  7. package/lib/cicd.js.map +1 -0
  8. package/lib/commits.d.ts +34 -0
  9. package/lib/commits.js +101 -0
  10. package/lib/commits.js.map +1 -0
  11. package/lib/config.d.ts +21 -0
  12. package/lib/config.js +76 -0
  13. package/lib/config.js.map +1 -0
  14. package/lib/env-file.d.ts +11 -0
  15. package/lib/env-file.js +98 -0
  16. package/lib/env-file.js.map +1 -0
  17. package/lib/errors.d.ts +31 -0
  18. package/lib/errors.js +66 -0
  19. package/lib/errors.js.map +1 -0
  20. package/lib/gitlab-client.d.ts +137 -0
  21. package/lib/gitlab-client.js +250 -0
  22. package/lib/gitlab-client.js.map +1 -0
  23. package/lib/http.d.ts +82 -0
  24. package/lib/http.js +285 -0
  25. package/lib/http.js.map +1 -0
  26. package/lib/index.d.ts +17 -0
  27. package/lib/index.js +46 -0
  28. package/lib/index.js.map +1 -0
  29. package/lib/json-input.d.ts +28 -0
  30. package/lib/json-input.js +84 -0
  31. package/lib/json-input.js.map +1 -0
  32. package/lib/merge-requests.d.ts +40 -0
  33. package/lib/merge-requests.js +89 -0
  34. package/lib/merge-requests.js.map +1 -0
  35. package/lib/output.d.ts +14 -0
  36. package/lib/output.js +65 -0
  37. package/lib/output.js.map +1 -0
  38. package/lib/redaction.d.ts +26 -0
  39. package/lib/redaction.js +71 -0
  40. package/lib/redaction.js.map +1 -0
  41. package/lib/repository-files.d.ts +50 -0
  42. package/lib/repository-files.js +70 -0
  43. package/lib/repository-files.js.map +1 -0
  44. package/lib/search.d.ts +20 -0
  45. package/lib/search.js +92 -0
  46. package/lib/search.js.map +1 -0
  47. package/lib/tables.d.ts +21 -0
  48. package/lib/tables.js +105 -0
  49. package/lib/tables.js.map +1 -0
  50. package/lib/testing.d.ts +16 -0
  51. package/lib/testing.js +63 -0
  52. package/lib/testing.js.map +1 -0
  53. package/lib/text.d.ts +10 -0
  54. package/lib/text.js +80 -0
  55. package/lib/text.js.map +1 -0
  56. package/lib/write-safety.d.ts +19 -0
  57. package/lib/write-safety.js +45 -0
  58. package/lib/write-safety.js.map +1 -0
  59. package/package.json +130 -0
package/lib/errors.js ADDED
@@ -0,0 +1,66 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Base class for every error this CLI raises deliberately. Commands surface these through
18
+ * oclif's `this.error(message, { exit })` so users get formatted output rather than a stack
19
+ * trace for expected failure modes.
20
+ */
21
+ export class CliError extends Error {
22
+ exitCode;
23
+ constructor(message, exitCode = 1) {
24
+ super(message);
25
+ this.name = 'CliError';
26
+ this.exitCode = exitCode;
27
+ }
28
+ }
29
+ /** Missing, malformed, or self-contradictory configuration. */
30
+ export class ConfigError extends CliError {
31
+ constructor(message) {
32
+ super(message, 2);
33
+ this.name = 'ConfigError';
34
+ }
35
+ }
36
+ /**
37
+ * The instance rejected our credentials. Carries the status because 401 and 403 call for
38
+ * different responses: bad credentials versus a valid account without the needed permission.
39
+ */
40
+ export class AuthError extends CliError {
41
+ status;
42
+ constructor(message, status) {
43
+ super(message, 3);
44
+ this.name = 'AuthError';
45
+ this.status = status;
46
+ }
47
+ }
48
+ /** The instance never answered: timeout, DNS miss, refused connection, or an untrusted certificate. */
49
+ export class NetworkError extends CliError {
50
+ constructor(message) {
51
+ super(message, 1);
52
+ this.name = 'NetworkError';
53
+ }
54
+ }
55
+ /** Any other non-2xx response, carrying the status and whatever body came back. */
56
+ export class HttpError extends CliError {
57
+ status;
58
+ body;
59
+ constructor(message, status, body) {
60
+ super(message, 1);
61
+ this.name = 'HttpError';
62
+ this.status = status;
63
+ this.body = body;
64
+ }
65
+ }
66
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;GAIG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjB,QAAQ,CAAS;IAEjC,YAAmB,OAAe,EAAE,QAAQ,GAAG,CAAC;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED,+DAA+D;AAC/D,MAAM,OAAO,WAAY,SAAQ,QAAQ;IACvC,YAAmB,OAAe;QAChC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,SAAU,SAAQ,QAAQ;IACrB,MAAM,CAAS;IAE/B,YAAmB,OAAe,EAAE,MAAc;QAChD,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,uGAAuG;AACvG,MAAM,OAAO,YAAa,SAAQ,QAAQ;IACxC,YAAmB,OAAe;QAChC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AAED,mFAAmF;AACnF,MAAM,OAAO,SAAU,SAAQ,QAAQ;IACrB,MAAM,CAAS;IACf,IAAI,CAAU;IAE9B,YAAmB,OAAe,EAAE,MAAc,EAAE,IAAc;QAChE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,137 @@
1
+ import type { GitLabConfig } from './config.js';
2
+ /** Everything a caller needs after following pages: the items plus why paging stopped. */
3
+ export interface ListResult<T = unknown> {
4
+ readonly items: readonly T[];
5
+ /** The instance's reported match count, when it reports one. */
6
+ readonly total?: number;
7
+ readonly pages: number;
8
+ /** False when the caller's limit cut the results short. */
9
+ readonly complete: boolean;
10
+ }
11
+ export type ProjectRef = string | number;
12
+ export interface ListProjectsOptions {
13
+ readonly membership?: boolean;
14
+ readonly owned?: boolean;
15
+ readonly simple?: boolean;
16
+ readonly search?: string;
17
+ readonly orderBy?: string;
18
+ readonly sort?: 'asc' | 'desc';
19
+ }
20
+ export interface ListBranchesOptions {
21
+ readonly search?: string;
22
+ }
23
+ export interface ListTagsOptions {
24
+ readonly search?: string;
25
+ readonly orderBy?: string;
26
+ readonly sort?: 'asc' | 'desc';
27
+ }
28
+ export interface ListCommitsOptions {
29
+ readonly ref?: string;
30
+ readonly since?: string;
31
+ readonly until?: string;
32
+ readonly path?: string;
33
+ readonly author?: string;
34
+ readonly withStats?: boolean;
35
+ }
36
+ export type MergeRequestState = 'opened' | 'closed' | 'locked' | 'merged' | 'all';
37
+ export interface ListMergeRequestsOptions {
38
+ readonly state?: MergeRequestState;
39
+ readonly sourceBranch?: string;
40
+ readonly targetBranch?: string;
41
+ readonly search?: string;
42
+ readonly authorUsername?: string;
43
+ readonly reviewerUsername?: string;
44
+ readonly labels?: readonly string[];
45
+ readonly orderBy?: string;
46
+ readonly sort?: 'asc' | 'desc';
47
+ }
48
+ export type JobScope = 'created' | 'pending' | 'running' | 'failed' | 'success' | 'canceled' | 'skipped' | 'waiting_for_resource' | 'manual';
49
+ export interface ListJobsOptions {
50
+ readonly scope?: readonly JobScope[];
51
+ }
52
+ export interface ListEnvironmentsOptions {
53
+ readonly name?: string;
54
+ readonly search?: string;
55
+ readonly states?: 'available' | 'stopped';
56
+ }
57
+ export type SearchScope = 'projects' | 'issues' | 'merge_requests' | 'milestones' | 'wiki_blobs' | 'commits' | 'blobs' | 'notes' | 'users' | 'snippet_titles';
58
+ export interface SearchOptions {
59
+ readonly scope: SearchScope;
60
+ readonly search: string;
61
+ /** Narrow to one project. Mutually exclusive with `group`. */
62
+ readonly project?: ProjectRef;
63
+ /** Narrow to one group. Mutually exclusive with `project`. */
64
+ readonly group?: ProjectRef;
65
+ /** Only meaningful for the `blobs` scope: a `filename:`/`path:`-style filter GitLab understands. */
66
+ readonly ref?: string;
67
+ }
68
+ export interface FileOptions {
69
+ readonly ref: string;
70
+ }
71
+ export interface WriteFileInput {
72
+ readonly branch: string;
73
+ readonly content: string;
74
+ readonly commitMessage: string;
75
+ readonly encoding?: 'text' | 'base64';
76
+ readonly authorEmail?: string;
77
+ readonly authorName?: string;
78
+ readonly startBranch?: string;
79
+ /** Update only: the last known commit id for the file, so a concurrent edit is rejected. */
80
+ readonly lastCommitId?: string;
81
+ }
82
+ export interface CommitAction {
83
+ readonly action: 'create' | 'delete' | 'move' | 'update' | 'chmod';
84
+ readonly file_path: string;
85
+ readonly previous_path?: string;
86
+ readonly content?: string;
87
+ readonly encoding?: 'text' | 'base64';
88
+ readonly last_commit_id?: string;
89
+ readonly execute_filemode?: boolean;
90
+ }
91
+ /**
92
+ * GitLab REST client. Owns the two things callers should never have to think about: addressing a
93
+ * project by path rather than id, and following offset pagination to a caller's limit while
94
+ * reporting honestly whether anything was left behind.
95
+ */
96
+ export declare class GitLabClient {
97
+ /** The instance this client talks to, for callers that need to render a URL beside a result. */
98
+ readonly url: string;
99
+ private readonly transport;
100
+ constructor(config: GitLabConfig);
101
+ listProjects(options?: ListProjectsOptions, limit?: number): Promise<ListResult>;
102
+ getProject(project: ProjectRef): Promise<unknown>;
103
+ getFile(project: ProjectRef, filePath: string, options: FileOptions): Promise<unknown>;
104
+ createFile(project: ProjectRef, filePath: string, input: WriteFileInput): Promise<unknown>;
105
+ updateFile(project: ProjectRef, filePath: string, input: WriteFileInput): Promise<unknown>;
106
+ listBranches(project: ProjectRef, options?: ListBranchesOptions, limit?: number): Promise<ListResult>;
107
+ createBranch(project: ProjectRef, branch: string, ref: string): Promise<unknown>;
108
+ listCommits(project: ProjectRef, options?: ListCommitsOptions, limit?: number): Promise<ListResult>;
109
+ getCommit(project: ProjectRef, sha: string): Promise<unknown>;
110
+ getCommitDiff(project: ProjectRef, sha: string): Promise<unknown>;
111
+ createCommit(project: ProjectRef, body: Record<string, unknown>): Promise<unknown>;
112
+ listMergeRequests(project: ProjectRef, options?: ListMergeRequestsOptions, limit?: number): Promise<ListResult>;
113
+ getMergeRequest(project: ProjectRef, iid: number): Promise<unknown>;
114
+ createMergeRequest(project: ProjectRef, body: Record<string, unknown>): Promise<unknown>;
115
+ updateMergeRequest(project: ProjectRef, iid: number, body: Record<string, unknown>): Promise<unknown>;
116
+ listTags(project: ProjectRef, options?: ListTagsOptions, limit?: number): Promise<ListResult>;
117
+ listJobs(project: ProjectRef, options?: ListJobsOptions, limit?: number): Promise<ListResult>;
118
+ /** A job's trace is plain text, sometimes megabytes of it, and is never JSON. */
119
+ getJobLog(project: ProjectRef, jobId: number): Promise<string>;
120
+ listVariables(project: ProjectRef, limit?: number): Promise<ListResult>;
121
+ listEnvironments(project: ProjectRef, options?: ListEnvironmentsOptions, limit?: number): Promise<ListResult>;
122
+ listReleases(project: ProjectRef, limit?: number): Promise<ListResult>;
123
+ /**
124
+ * Global, group, or project search. GitLab exposes these as three separate endpoints rather
125
+ * than one endpoint with a filter, and the scopes each accepts differ, so the caller's choice
126
+ * of `project` or `group` selects the route instead of becoming a query parameter.
127
+ */
128
+ search(options: SearchOptions, limit?: number): Promise<ListResult>;
129
+ private request;
130
+ /**
131
+ * Follows offset pagination until the limit is reached or the instance says there is no next
132
+ * page, and reports which of the two happened. A caller that gets `complete: false` was cut
133
+ * short by its own limit and knows more exists — the alternative, returning a short list with
134
+ * no signal, is how a partial answer gets mistaken for the whole one.
135
+ */
136
+ private collect;
137
+ }
@@ -0,0 +1,250 @@
1
+ /*
2
+ * Copyright (c) 2026, SimplySF.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { buildAuthHeaders } from './auth.js';
17
+ import { ConfigError } from './errors.js';
18
+ import { HttpTransport } from './http.js';
19
+ /** GitLab's own ceiling on `per_page`; asking for more is silently clamped to it. */
20
+ const MAX_PER_PAGE = 100;
21
+ /** How many pages one list call will follow before giving up, however small the page size. */
22
+ const MAX_PAGES = 100;
23
+ const API_BASE = '/api/v4';
24
+ /**
25
+ * A path segment built from user input. GitLab addresses a project either by numeric id or by its
26
+ * URL-encoded full path, so `group/project` has to arrive as `group%2Fproject` — an un-encoded
27
+ * slash reads as a different route entirely and answers 404 with nothing pointing at the cause.
28
+ */
29
+ function segment(value) {
30
+ const text = String(value).trim();
31
+ if (text === '')
32
+ throw new ConfigError('A project id or path is required, but an empty value was given.');
33
+ return encodeURIComponent(text);
34
+ }
35
+ /** Drops the entries a caller left unset so they never reach the query string as "undefined". */
36
+ function query(pairs) {
37
+ return Object.fromEntries(Object.entries(pairs).filter(([, value]) => value !== undefined));
38
+ }
39
+ /**
40
+ * GitLab REST client. Owns the two things callers should never have to think about: addressing a
41
+ * project by path rather than id, and following offset pagination to a caller's limit while
42
+ * reporting honestly whether anything was left behind.
43
+ */
44
+ export class GitLabClient {
45
+ /** The instance this client talks to, for callers that need to render a URL beside a result. */
46
+ url;
47
+ transport;
48
+ constructor(config) {
49
+ this.url = config.url;
50
+ this.transport = new HttpTransport({
51
+ baseUrl: config.url,
52
+ headers: buildAuthHeaders(config),
53
+ });
54
+ }
55
+ // --- Projects ---
56
+ listProjects(options = {}, limit = 20) {
57
+ return this.collect('/projects', query({
58
+ membership: options.membership,
59
+ owned: options.owned,
60
+ simple: options.simple,
61
+ search: options.search,
62
+ order_by: options.orderBy,
63
+ sort: options.sort,
64
+ }), limit);
65
+ }
66
+ async getProject(project) {
67
+ return this.request(`/projects/${segment(project)}`, { method: 'GET' });
68
+ }
69
+ // --- Repository files ---
70
+ async getFile(project, filePath, options) {
71
+ return this.request(`/projects/${segment(project)}/repository/files/${segment(filePath)}`, {
72
+ method: 'GET',
73
+ query: { ref: options.ref },
74
+ });
75
+ }
76
+ async createFile(project, filePath, input) {
77
+ return this.request(`/projects/${segment(project)}/repository/files/${segment(filePath)}`, {
78
+ method: 'POST',
79
+ body: fileBody(input),
80
+ mutating: true,
81
+ });
82
+ }
83
+ async updateFile(project, filePath, input) {
84
+ return this.request(`/projects/${segment(project)}/repository/files/${segment(filePath)}`, {
85
+ method: 'PUT',
86
+ body: fileBody(input),
87
+ mutating: true,
88
+ });
89
+ }
90
+ // --- Branches ---
91
+ async listBranches(project, options = {}, limit = 20) {
92
+ return this.collect(`/projects/${segment(project)}/repository/branches`, query({ search: options.search }), limit);
93
+ }
94
+ async createBranch(project, branch, ref) {
95
+ return this.request(`/projects/${segment(project)}/repository/branches`, {
96
+ method: 'POST',
97
+ body: { branch, ref },
98
+ mutating: true,
99
+ });
100
+ }
101
+ // --- Commits ---
102
+ async listCommits(project, options = {}, limit = 20) {
103
+ return this.collect(`/projects/${segment(project)}/repository/commits`, query({
104
+ ref_name: options.ref,
105
+ since: options.since,
106
+ until: options.until,
107
+ path: options.path,
108
+ author: options.author,
109
+ with_stats: options.withStats,
110
+ }), limit);
111
+ }
112
+ async getCommit(project, sha) {
113
+ return this.request(`/projects/${segment(project)}/repository/commits/${segment(sha)}`, { method: 'GET' });
114
+ }
115
+ async getCommitDiff(project, sha) {
116
+ return this.request(`/projects/${segment(project)}/repository/commits/${segment(sha)}/diff`, { method: 'GET' });
117
+ }
118
+ async createCommit(project, body) {
119
+ return this.request(`/projects/${segment(project)}/repository/commits`, { method: 'POST', body, mutating: true });
120
+ }
121
+ // --- Merge requests ---
122
+ async listMergeRequests(project, options = {}, limit = 20) {
123
+ return this.collect(`/projects/${segment(project)}/merge_requests`, query({
124
+ state: options.state,
125
+ source_branch: options.sourceBranch,
126
+ target_branch: options.targetBranch,
127
+ search: options.search,
128
+ author_username: options.authorUsername,
129
+ reviewer_username: options.reviewerUsername,
130
+ labels: options.labels === undefined ? undefined : options.labels.join(','),
131
+ order_by: options.orderBy,
132
+ sort: options.sort,
133
+ }), limit);
134
+ }
135
+ async getMergeRequest(project, iid) {
136
+ return this.request(`/projects/${segment(project)}/merge_requests/${iid}`, { method: 'GET' });
137
+ }
138
+ async createMergeRequest(project, body) {
139
+ return this.request(`/projects/${segment(project)}/merge_requests`, { method: 'POST', body, mutating: true });
140
+ }
141
+ async updateMergeRequest(project, iid, body) {
142
+ return this.request(`/projects/${segment(project)}/merge_requests/${iid}`, {
143
+ method: 'PUT',
144
+ body,
145
+ mutating: true,
146
+ });
147
+ }
148
+ // --- Tags ---
149
+ async listTags(project, options = {}, limit = 20) {
150
+ return this.collect(`/projects/${segment(project)}/repository/tags`, query({ search: options.search, order_by: options.orderBy, sort: options.sort }), limit);
151
+ }
152
+ // --- CI/CD ---
153
+ async listJobs(project, options = {}, limit = 20) {
154
+ return this.collect(`/projects/${segment(project)}/jobs`, query({ scope: options.scope }), limit);
155
+ }
156
+ /** A job's trace is plain text, sometimes megabytes of it, and is never JSON. */
157
+ async getJobLog(project, jobId) {
158
+ return this.transport.text({
159
+ method: 'GET',
160
+ path: `${API_BASE}/projects/${segment(project)}/jobs/${jobId}/trace`,
161
+ });
162
+ }
163
+ async listVariables(project, limit = 20) {
164
+ return this.collect(`/projects/${segment(project)}/variables`, {}, limit);
165
+ }
166
+ async listEnvironments(project, options = {}, limit = 20) {
167
+ return this.collect(`/projects/${segment(project)}/environments`, query({ name: options.name, search: options.search, states: options.states }), limit);
168
+ }
169
+ // --- Releases ---
170
+ async listReleases(project, limit = 20) {
171
+ return this.collect(`/projects/${segment(project)}/releases`, {}, limit);
172
+ }
173
+ // --- Search ---
174
+ /**
175
+ * Global, group, or project search. GitLab exposes these as three separate endpoints rather
176
+ * than one endpoint with a filter, and the scopes each accepts differ, so the caller's choice
177
+ * of `project` or `group` selects the route instead of becoming a query parameter.
178
+ */
179
+ async search(options, limit = 20) {
180
+ if (options.project !== undefined && options.group !== undefined) {
181
+ throw new ConfigError('Search within a project or within a group, not both.');
182
+ }
183
+ const path = options.project !== undefined
184
+ ? `/projects/${segment(options.project)}/search`
185
+ : options.group !== undefined
186
+ ? `/groups/${segment(options.group)}/search`
187
+ : '/search';
188
+ return this.collect(path, query({ scope: options.scope, search: options.search, ref: options.ref }), limit);
189
+ }
190
+ // --- Plumbing ---
191
+ request(path, call) {
192
+ return this.transport.json({ ...call, path: `${API_BASE}${path}` });
193
+ }
194
+ /**
195
+ * Follows offset pagination until the limit is reached or the instance says there is no next
196
+ * page, and reports which of the two happened. A caller that gets `complete: false` was cut
197
+ * short by its own limit and knows more exists — the alternative, returning a short list with
198
+ * no signal, is how a partial answer gets mistaken for the whole one.
199
+ */
200
+ async collect(path, params, limit) {
201
+ if (!Number.isInteger(limit) || limit < 1) {
202
+ throw new ConfigError(`A limit must be a positive whole number, got ${limit}.`);
203
+ }
204
+ const items = [];
205
+ let page = 1;
206
+ let pages = 0;
207
+ let total;
208
+ /* Paging is sequential by definition: each request needs the previous page's cursor. */
209
+ /* eslint-disable no-await-in-loop */
210
+ while (items.length < limit && pages < MAX_PAGES) {
211
+ const response = await this.transport.jsonPaged({
212
+ method: 'GET',
213
+ path: `${API_BASE}${path}`,
214
+ query: { ...params, per_page: Math.min(MAX_PER_PAGE, limit - items.length), page },
215
+ });
216
+ pages += 1;
217
+ total = response.pagination.total ?? total;
218
+ // Every list endpoint answers with a JSON array. A non-array here means the route matched
219
+ // something else — a redirect to a sign-in page, most often — and slicing it would produce
220
+ // a confidently empty list rather than an error.
221
+ if (!Array.isArray(response.data)) {
222
+ throw new ConfigError(`GET ${path} did not return a list. Check that the URL points at a GitLab instance.`);
223
+ }
224
+ items.push(...response.data);
225
+ const nextPage = response.pagination.nextPage;
226
+ // No next-page header, an empty page, or a cursor that fails to advance all mean the same
227
+ // thing: this was the last page. The last of the three matters because a misconfigured
228
+ // reverse proxy that strips or pins the header would otherwise loop until MAX_PAGES.
229
+ if (response.data.length === 0 || nextPage === undefined || nextPage <= page) {
230
+ return { items: items.slice(0, limit), total, pages, complete: true };
231
+ }
232
+ page = nextPage;
233
+ }
234
+ /* eslint-enable no-await-in-loop */
235
+ return { items: items.slice(0, limit), total, pages, complete: false };
236
+ }
237
+ }
238
+ function fileBody(input) {
239
+ return {
240
+ branch: input.branch,
241
+ content: input.content,
242
+ commit_message: input.commitMessage,
243
+ ...(input.encoding === undefined ? {} : { encoding: input.encoding }),
244
+ ...(input.authorEmail === undefined ? {} : { author_email: input.authorEmail }),
245
+ ...(input.authorName === undefined ? {} : { author_name: input.authorName }),
246
+ ...(input.startBranch === undefined ? {} : { start_branch: input.startBranch }),
247
+ ...(input.lastCommitId === undefined ? {} : { last_commit_id: input.lastCommitId }),
248
+ };
249
+ }
250
+ //# sourceMappingURL=gitlab-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitlab-client.js","sourceRoot":"","sources":["../src/gitlab-client.ts"],"names":[],"mappings":"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"}
package/lib/http.d.ts ADDED
@@ -0,0 +1,82 @@
1
+ /** A single query value, or a repeated one — GitLab spells repeats `scope[]=a&scope[]=b`. */
2
+ export type QueryValue = string | number | boolean | undefined | ReadonlyArray<string | number>;
3
+ /** One REST call, relative to the transport's base URL. */
4
+ export interface JsonCall {
5
+ readonly method: string;
6
+ readonly path: string;
7
+ readonly query?: Record<string, QueryValue>;
8
+ readonly body?: unknown;
9
+ /**
10
+ * Whether this call changes data. Declared by the caller rather than guessed from the verb,
11
+ * because the verb is not a reliable signal: GitLab's global search is a GET that a `read_api`
12
+ * token can run, while its markdown renderer is a POST that changes nothing. Guessing wrong
13
+ * here is worse than not guessing — see the 403 handling below.
14
+ */
15
+ readonly mutating?: boolean;
16
+ }
17
+ /** Where and how a transport talks: fixed per client instance. */
18
+ export interface TransportTarget {
19
+ readonly baseUrl: string;
20
+ readonly headers: Record<string, string>;
21
+ readonly timeoutMs?: number;
22
+ readonly maxAttempts?: number;
23
+ }
24
+ /**
25
+ * GitLab's offset-pagination headers, as far as they were sent. Keyset-paginated endpoints and
26
+ * any endpoint whose result set is too large for GitLab to count omit the totals, so every field
27
+ * here is optional and a caller must treat a missing one as "unknown", never as zero.
28
+ */
29
+ export interface Pagination {
30
+ readonly nextPage?: number;
31
+ readonly total?: number;
32
+ readonly totalPages?: number;
33
+ readonly perPage?: number;
34
+ }
35
+ /** A response body together with the pagination GitLab reported alongside it. */
36
+ export interface Paged<T> {
37
+ readonly data: T;
38
+ readonly pagination: Pagination;
39
+ }
40
+ /**
41
+ * JSON-over-HTTPS transport bound to one GitLab instance. Each client owns one; everything that
42
+ * would otherwise be an argument (base URL, auth headers, timing) is fixed at construction so
43
+ * call sites stay small.
44
+ *
45
+ * Certificate verification is always on. An instance behind an internal or agency CA is
46
+ * supported by pointing Node at that CA — `NODE_EXTRA_CA_CERTS=/path/to/ca.pem` — which keeps
47
+ * verification intact rather than turning it off.
48
+ */
49
+ export declare class HttpTransport {
50
+ private readonly target;
51
+ private readonly timeoutMs;
52
+ private readonly maxAttempts;
53
+ constructor(target: TransportTarget);
54
+ /**
55
+ * Executes a call and parses the JSON response. 401/403 become `AuthError` immediately;
56
+ * 429/5xx retry with exponential backoff, deferring to a (capped) `Retry-After` when one is
57
+ * sent; other non-2xx become `HttpError`. Timeouts, DNS misses, and certificate failures
58
+ * become `NetworkError` without pointless retries; transient socket failures retry. The
59
+ * timeout covers the whole exchange, response body included.
60
+ */
61
+ json<T>(call: JsonCall): Promise<T>;
62
+ /**
63
+ * The same call as {@link json}, keeping the pagination headers GitLab answered with. Separate
64
+ * from `json` because pagination is only meaningful for list endpoints, and a caller that
65
+ * ignored the headers on one would silently return a first page as if it were everything.
66
+ */
67
+ jsonPaged<T>(call: JsonCall): Promise<Paged<T>>;
68
+ /**
69
+ * Executes a call whose response is not JSON and returns it verbatim.
70
+ *
71
+ * Job traces are plain text and repository file contents can be raw bytes; parsing either as
72
+ * JSON would fail on the successful case. Errors on these endpoints still come back as JSON,
73
+ * and are still raised as `HttpError`, because the failure path is shared.
74
+ */
75
+ text(call: JsonCall): Promise<string>;
76
+ private send;
77
+ /** Runs one attempt under a deadline that spans connection, headers, AND body. */
78
+ private attempt;
79
+ /** Wraps raw transport failures in typed, user-explainable errors; rethrows deliberate ones. */
80
+ private triage;
81
+ private resolve;
82
+ }