@gitbeaker/requester-utils 0.0.0-canary-20260118165457

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/README.md ADDED
@@ -0,0 +1,246 @@
1
+ <div align="center">
2
+ <br>
3
+ <br>
4
+ <img alt="gitbeaker" src="../../.github/ASSETS/header.svg">
5
+ <br>
6
+ <br>
7
+ <br>
8
+ </div>
9
+ <hr>
10
+ <div align="center">
11
+ <p>
12
+ <sup>
13
+ <a href="https://github.com/jdalrymple">My open source work is supported by the community</a>
14
+ </sup>
15
+ </p>
16
+ <br>
17
+ <sup>Special thanks to:</sup>
18
+ <br>
19
+ <br>
20
+ <a href="http://coderabbit.ai">
21
+ <img src="../../.github/ASSETS/coderabbit.png" width="180">
22
+ </a>
23
+ <br>
24
+ <br>
25
+ <hr>
26
+ </div>
27
+
28
+ <p align="center">
29
+ <a href="https://dl.circleci.com/status-badge/redirect/gh/jdalrymple/gitbeaker/tree/main">
30
+ <img alt="CircleCI" src="https://dl.circleci.com/status-badge/img/gh/jdalrymple/gitbeaker/tree/main.svg?style=svg" />
31
+ </a>
32
+ <a href="https://codecov.io/gh/jdalrymple/gitbeaker">
33
+ <img alt="Requester Utils Coverage" src="https://img.shields.io/codecov/c/github/jdalrymple/gitbeaker?flag=requester-utils&logo=codecov&label=coverage"/>
34
+ </a>
35
+
36
+ <a href="https://github.com/intuit/auto">
37
+ <img src="https://img.shields.io/badge/release-auto.svg?colorA=888888&colorB=9B065A&label=auto" alt="Auto">
38
+ </a>
39
+ <a href="#contributors-">
40
+ <img src="https://img.shields.io/badge/all_contributors-orange.svg?style=round" alt="All Contributors" />
41
+ </a>
42
+ <img src="https://img.shields.io/badge/code%20style-prettier-ff69b4.svg" alt="Prettier">
43
+ <a href="LICENSE.md">
44
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="Licence: MIT">
45
+ </a>
46
+ <a href="https://packagephobia.com/result?p=@gitbeaker/requester-utils">
47
+ <img src="https://packagephobia.com/badge?p=@gitbeaker/requester-utils" alt="Install Size: Requester Utils">
48
+ </a>
49
+ </p>
50
+
51
+ > Utility for creating custom wrappers around the [@gitbeaker/core](https://www.npmjs.com/package/@gitbeaker/core) [GitLab](https://gitlab.com/gitlab-org/gitlab/) SDK.
52
+
53
+ ## Table of Contents
54
+
55
+ - [Usage](#usage)
56
+ - [Testing](../../docs/TESTING.md)
57
+ - [FAQ](../../docs/FAQ.md)
58
+ - [Contributors](#contributors)
59
+ - [Changelog](./CHANGELOG.md)
60
+
61
+ ## Usage
62
+
63
+ <table>
64
+ <tbody valign=top align=left>
65
+ <tr><th>
66
+ Browsers
67
+ </th><td width=100%>
68
+ Load <code>@gitbeaker/requester-utils</code> directly from <a href="https://esm.sh">esm.sh</a>
69
+
70
+ ```html
71
+ <script type="module">
72
+ import { RequesterUtils, BaseResource } from 'https://esm.sh/@gitbeaker/requester-utils';
73
+ </script>
74
+ ```
75
+
76
+ </td></tr>
77
+ <tr><th>
78
+ Deno
79
+ </th><td width=100%>
80
+ Load <code>@gitbeaker/requester-utils</code> directly from <a href="https://esm.sh">esm.sh</a>
81
+
82
+ ```ts
83
+ import { RequesterUtils, BaseResource } from 'https://esm.sh/@gitbeaker/requester-utils?dts';
84
+ ```
85
+
86
+ </td></tr>
87
+ <tr><th>
88
+ Node 18+
89
+ </th><td>
90
+
91
+ Install with <code>npm install @gitbeaker/requester-utils</code>, or <code>yarn add @gitbeaker/requester-utils</code>
92
+
93
+ ```js
94
+ import { RequesterUtils, BaseResource } from '@gitbeaker/requester-utils';
95
+ ```
96
+
97
+ </td></tr>
98
+ </tbody>
99
+ </table>
100
+
101
+ ## Contributors
102
+
103
+ <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
104
+ <!-- prettier-ignore-start -->
105
+ <!-- markdownlint-disable -->
106
+ <p>
107
+ <tr>
108
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/jdalrymple"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/3743662?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Justin Dalrymple"/></td>
109
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/dylandesrosier"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13701258?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Dylan DesRosier"/></td>
110
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/mikew"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4729?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Mike Wyatt"/></td>
111
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/coryzibell"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7986014?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Cory Zibeill"/></td>
112
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/shadygrove"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5209850?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Martin Bour"/></td>
113
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/akira345"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/655764?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="akira345"/></td>
114
+ <td align="center" valign="top" width="0.33%"><a href="https://birukov.me"><img src="https://images.weserv.nl/?url=https://avatars2.githubusercontent.com/u/1861546?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Pavel Birukov "/></td>
115
+ <td align="center" valign="top" width="0.33%"><a href="http://jetersen.dev"><img src="https://images.weserv.nl/?url=https://avatars2.githubusercontent.com/u/1661688?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Joseph Petersen"/></td>
116
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Musinux"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/563373?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Louis Cherel"/></td>
117
+ <td align="center" valign="top" width="0.33%"><a href="http://www.arsdehnel.net"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/1697162?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Adam Dehnel"/></td>
118
+ <td align="center" valign="top" width="0.33%"><a href="http://www.haus.gg/"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/226640?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Ev Haus"/></td>
119
+ <td align="center" valign="top" width="0.33%"><a href="http://iGLOO.be"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/900947?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Loïc Mahieu"/></td>
120
+ <td align="center" valign="top" width="0.33%"><a href="http://www.giuseppeangri.com"><img src="https://images.weserv.nl/?url=https://avatars2.githubusercontent.com/u/9075163?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Giuseppe Angri"/></td>
121
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/jennparise"><img src="https://images.weserv.nl/?url=https://avatars1.githubusercontent.com/u/4134086?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="jennparise"/></td>
122
+ <td align="center" valign="top" width="0.33%"><a href="http://obartra.github.io"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/3877773?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Oscar"/></td>
123
+ <td align="center" valign="top" width="0.33%"><a href="https://daniel-ruf.de"><img src="https://images.weserv.nl/?url=https://avatars1.githubusercontent.com/u/827205?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Daniel Ruf"/></td>
124
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/schindld"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/1659632?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="schindld"/></td>
125
+ <td align="center" valign="top" width="0.33%"><a href="https://alvarobg.com"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/12004383?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Alvaro"/></td>
126
+ <td align="center" valign="top" width="0.33%"><a href="http://northhorizon.net"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/616152?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Daniel Moore"/></td>
127
+ <td align="center" valign="top" width="0.33%"><a href="https://dylanmtaylor.com"><img src="https://images.weserv.nl/?url=https://avatars2.githubusercontent.com/u/277927?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Dylan M. Taylor"/></td>
128
+ <td align="center" valign="top" width="0.33%"><a href="https://pixelswap.fr/"><img src="https://images.weserv.nl/?url=https://avatars1.githubusercontent.com/u/4266283?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Corentin Mors"/></td>
129
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/xatavian"><img src="https://images.weserv.nl/?url=https://avatars1.githubusercontent.com/u/17217965?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="xatavian"/></td>
130
+ <td align="center" valign="top" width="0.33%"><a href="https://stackoverflow.com/story/yepninja"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/11796206?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Yevgeny Petukhov"/></td>
131
+ <td align="center" valign="top" width="0.33%"><a href="https://about.me/mickaeltr"><img src="https://images.weserv.nl/?url=https://avatars2.githubusercontent.com/u/378910?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Mickaël Tricot"/></td>
132
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/andreasciamanna"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/181780?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Andrea"/></td>
133
+ <td align="center" valign="top" width="0.33%"><a href="http://www.ircad.fr/"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/8638653?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Flavien Bridault"/></td>
134
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/s-kazuki"><img src="https://images.weserv.nl/?url=https://avatars2.githubusercontent.com/u/9253374?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="s-kazuki"/></td>
135
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/kiprasmel"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/29430509?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Kipras Melnikovas"/></td>
136
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Gkxie"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/27680715?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="xieyu"/></td>
137
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/st1gok"><img src="https://images.weserv.nl/?url=https://avatars1.githubusercontent.com/u/13641693?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="st1gok"/></td>
138
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/max-wittig"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/6639323?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Max Wittig"/></td>
139
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/nlochschmidt"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/759624?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Niklas Lochschmidt"/></td>
140
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/illyaMs"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/26578665?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Ilya Dus"/></td>
141
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/wamry"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/32439651?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Omar Awamry"/></td>
142
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Sumragen"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/15640910?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Hennadii Varava"/></td>
143
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/xiezht"><img src="https://images.weserv.nl/?url=https://avatars1.githubusercontent.com/u/18051618?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="xiezht"/></td>
144
+ <td align="center" valign="top" width="0.33%"><a href="http://www.martin-helmich.de/en"><img src="https://images.weserv.nl/?url=https://avatars3.githubusercontent.com/u/2538958?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Martin Helmich"/></td>
145
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/smcgivern"><img src="https://images.weserv.nl/?url=https://avatars0.githubusercontent.com/u/1120328?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Sean McGivern"/></td>
146
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Vogel612"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7288995?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Clemens Lieb"/></td>
147
+ <td align="center" valign="top" width="0.33%"><a href="https://sajdl.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5222912?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Vojtěch Sajdl"/></td>
148
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/divido"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4614626?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="divido"/></td>
149
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/vboulaye"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/652767?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Vincent Boulaye"/></td>
150
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Aliyss"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/33941859?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Aliyss Snow"/></td>
151
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/saada"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1087987?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Mahmoud Saada"/></td>
152
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/F3n67u"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/12343178?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Feng Yu"/></td>
153
+ <td align="center" valign="top" width="0.33%"><a href="https://datatra.sh/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/173822?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Roy Jacobs"/></td>
154
+ <td align="center" valign="top" width="0.33%"><a href="http://www.paullemke.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/976010?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Paul Lemke"/></td>
155
+ <td align="center" valign="top" width="0.33%"><a href="http://jenko.me/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/131355?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Ian Jenkins"/></td>
156
+ <td align="center" valign="top" width="0.33%"><a href="https://www.nilennoct.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4055220?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="nilennoct"/></td>
157
+ <td align="center" valign="top" width="0.33%"><a href="http://michael.laffargue.fr/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/503129?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Laffargue Michael"/></td>
158
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/MartinHowarth"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7187425?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Martin Howarth"/></td>
159
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/christophlehmann"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4953689?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Christoph Lehmann"/></td>
160
+ <td align="center" valign="top" width="0.33%"><a href="https://theopensourceu.org/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/253471?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Frank V"/></td>
161
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Salimlou"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/357286?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Salim Benabbou"/></td>
162
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/tvtamas"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1945260?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Tamás Török-Vistai"/></td>
163
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/MartinBenninger"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/20296116?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Martin Benninger"/></td>
164
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/fewieden"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9334168?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="fewieden"/></td>
165
+ <td align="center" valign="top" width="0.33%"><a href="https://www.jeffpelton.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/36627?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Jeff Pelton"/></td>
166
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/claude-abounegm"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11809881?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Claude Abounegm"/></td>
167
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Marethyu1"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/17978203?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Stefan Hall"/></td>
168
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Mr-Wallet"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/799000?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Jordan Wallet"/></td>
169
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/zhao0"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7556666?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="zhao0"/></td>
170
+ <td align="center" valign="top" width="0.33%"><a href="https://www.linkedin.com/in/joshuagrosso"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4530584?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Joshua Grosso"/></td>
171
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/yonguelink"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9469187?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Isaac Ouellet Therrien"/></td>
172
+ <td align="center" valign="top" width="0.33%"><a href="https://wearecws.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/24895138?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Rajat Sharma"/></td>
173
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Casz"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/65105345?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Cesar B."/></td>
174
+ <td align="center" valign="top" width="0.33%"><a href="https://blog.katsuba.dev/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10637135?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Igor Katsuba"/></td>
175
+ <td align="center" valign="top" width="0.33%"><a href="http://www.doublespeakgames.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4884483?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Michael Townsend"/></td>
176
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/bodtx"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1039550?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="bodtx"/></td>
177
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/arthot"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1815294?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Artem"/></td>
178
+ <td align="center" valign="top" width="0.33%"><a href="https://muniftanjim.dev/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8050659?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Munif Tanjim"/></td>
179
+ <td align="center" valign="top" width="0.33%"><a href="https://www.qkdreyer.dev/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/717869?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Quentin Dreyer"/></td>
180
+ <td align="center" valign="top" width="0.33%"><a href="https://iwritethe.codes/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/192728?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Norm MacLennan"/></td>
181
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/jnovick"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/7881319?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="jnovick"/></td>
182
+ <td align="center" valign="top" width="0.33%"><a href="https://www.fabianaussems.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/57530?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Fabian Aussems"/></td>
183
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/mima0815"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/14311597?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Michael Matzka"/></td>
184
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/CraigAllardyce"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9052289?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="CraigAllardyce"/></td>
185
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/brunobastosg"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/320122?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Bruno Guimarães"/></td>
186
+ <td align="center" valign="top" width="0.33%"><a href="http://leipert.io/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/2906107?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Lukas Eipert"/></td>
187
+ <td align="center" valign="top" width="0.33%"><a href="https://max.krauss.io/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/914671?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Maximilian Krauß"/></td>
188
+ <td align="center" valign="top" width="0.33%"><a href="https://eng.evolution.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/15799569?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Evolution Engineering"/></td>
189
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Neonox31"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1135958?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="WEBER Logan"/></td>
190
+ <td align="center" valign="top" width="0.33%"><a href="https://t.me/mister_cheater"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5055654?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Anton Zhukov"/></td>
191
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/beaverusiv"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4149031?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Nicholas Loomans"/></td>
192
+ <td align="center" valign="top" width="0.33%"><a href="https://icedream.pw/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/807772?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Carl Kittelberger"/></td>
193
+ <td align="center" valign="top" width="0.33%"><a href="https://patrik.votocek.cz/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/112567?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Patrik Votoček"/></td>
194
+ <td align="center" valign="top" width="0.33%"><a href="https://kyr.github.io/CV/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/426462?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Kyrylo Fedorov"/></td>
195
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/claudio-vellage"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/28930612?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Claudio Vellage"/></td>
196
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/seb0uil"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5122626?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Seb0uil"/></td>
197
+ <td align="center" valign="top" width="0.33%"><a href="http://merorafael.info/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3404989?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Rafael Mello"/></td>
198
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/tbazin"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9104039?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Théis Bazin"/></td>
199
+ <td align="center" valign="top" width="0.33%"><a href="http://spencersalisbury.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8053224?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Spencer Salisbury"/></td>
200
+ <td align="center" valign="top" width="0.33%"><a href="https://simonecorsi.dev/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5617452?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Simone Corsi"/></td>
201
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Bambii556"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/34485027?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Bambii"/></td>
202
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/ravewill"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/128541928?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Will"/></td>
203
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/ArnaudTA"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/33383276?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="ArnaudTA"/></td>
204
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/kouak"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/48335?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Benjamin Beret"/></td>
205
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/demedos"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/16702156?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Alessandro Diez"/></td>
206
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/artlist-scottambrose"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/124692101?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="artlist-scottambrose"/></td>
207
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/mercutiodesign"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1114120?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Martin Dreher"/></td>
208
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/glensc"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/199095?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Elan Ruusamäe"/></td>
209
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Artemoire"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/18062266?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="artemoire"/></td>
210
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/pataar"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3403851?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Pieter Willekens"/></td>
211
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Djcd"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/761764?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="David Claybourne"/></td>
212
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/domharrington"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/848223?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Dom Harrington"/></td>
213
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/kseino"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/1378066?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Kohei Seino"/></td>
214
+ <td align="center" valign="top" width="0.33%"><a href="https://www.1stg.me/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/8336744?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="JounQin"/></td>
215
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/elaine-mattos"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/79633988?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Elaine Mattos"/></td>
216
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/vitamindck"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/10766587?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="DK"/></td>
217
+ <td align="center" valign="top" width="0.33%"><a href="http://www.koenbrouwer.com/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3260168?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Koen Brouwer"/></td>
218
+ <td align="center" valign="top" width="0.33%"><a href="https://scrumplex.net/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/11587657?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Sefa Eyeoglu"/></td>
219
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/crazfb"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/157008803?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Carmi Raz"/></td>
220
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/unilynx"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/2772353?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Arnold Hendriks"/></td>
221
+ <td align="center" valign="top" width="0.33%"><a href="https://baylee.dev/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/119542904?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Baylee Schmeisser"/></td>
222
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/andys8"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/13085980?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Andy"/></td>
223
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/roberta-pavel"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/26488965?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="roberta-pavel"/></td>
224
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/yousinix"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/41103290?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Youssef"/></td>
225
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/mpsanchis"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/33475618?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Miguel"/></td>
226
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Retr0-01"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/61121754?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Giannis Kepas"/></td>
227
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/talyssonoc"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/4325587?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Talysson de Oliveira Cassiano"/></td>
228
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Sephyr0s"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/34128667?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Marco"/></td>
229
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/enduity"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/19674002?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Endrik Einberg"/></td>
230
+ <td align="center" valign="top" width="0.33%"><a href="https://note.itswhat.me/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/38807139?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Bryan Lee"/></td>
231
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/zk-kb4"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/42388953?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Zack"/></td>
232
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/kayw-geek"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/29700073?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Kay W."/></td>
233
+ <td align="center" valign="top" width="0.33%"><a href="https://ffflorian.dev/"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5497598?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Florian Imdahl"/></td>
234
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/lanthier"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/9666344?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="lanthier"/></td>
235
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/Teo-ShaoWei"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/6925907?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="ShaoWei Teo"/></td>
236
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/danielrentz"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/5064304?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Daniel Rentz"/></td>
237
+ <td align="center" valign="top" width="0.33%"><a href="https://github.com/matejsmisek"><img src="https://images.weserv.nl/?url=https://avatars.githubusercontent.com/u/3141623?v=4&h=25&w=25&fit=cover&mask=circle&maxage=7d" alt="Matěj Smíšek"/></td>
238
+ </tr>
239
+ </p>
240
+
241
+ <!-- markdownlint-restore -->
242
+ <!-- prettier-ignore-end -->
243
+
244
+ <!-- ALL-CONTRIBUTORS-LIST:END -->
245
+
246
+ This started as a fork from [node-gitlab-legacy](https://github.com/rest-gitlab/rest-gitlab-legacy) but I ended up rewriting much of the code. Here are the original work's [contributors](https://github.com/rest-gitlab/rest-gitlab-legacy#contributors).
@@ -0,0 +1,133 @@
1
+ import { Agent } from 'http';
2
+
3
+ type RateLimiterFn = () => Promise<number>;
4
+ type RateLimiters = Record<string, RateLimiterFn | {
5
+ method: string;
6
+ limit: RateLimiterFn;
7
+ }>;
8
+ type RateLimitOptions = Record<string, number | {
9
+ method: string;
10
+ limit: number;
11
+ }>;
12
+ type ResponseBodyTypes = Record<string, unknown> | Record<string, unknown>[] | ReadableStream | Blob | string | string[] | number | void | null;
13
+ interface FormattedResponse<T extends ResponseBodyTypes = ResponseBodyTypes> {
14
+ body: T;
15
+ headers: Record<string, string>;
16
+ status: number;
17
+ }
18
+ interface Constructable<T = any> {
19
+ new (...args: any[]): T;
20
+ }
21
+ type ResourceOptions = {
22
+ headers: {
23
+ [header: string]: string;
24
+ };
25
+ authHeaders: {
26
+ [authHeader: string]: () => Promise<string>;
27
+ };
28
+ url: string;
29
+ rateLimits?: RateLimitOptions;
30
+ rateLimitDuration?: number;
31
+ agent?: Agent;
32
+ };
33
+ type DefaultRequestOptions = {
34
+ body?: FormData | Record<string, unknown>;
35
+ searchParams?: Record<string, unknown>;
36
+ sudo?: string | number;
37
+ method?: string;
38
+ asStream?: boolean;
39
+ signal?: AbortSignal;
40
+ };
41
+ type RequestOptions = {
42
+ headers?: Record<string, string>;
43
+ timeout?: number;
44
+ method?: string;
45
+ searchParams?: string;
46
+ prefixUrl?: string;
47
+ body?: string | FormData;
48
+ asStream?: boolean;
49
+ signal?: AbortSignal;
50
+ rateLimiters?: Record<string, RateLimiterFn>;
51
+ agent?: Agent;
52
+ };
53
+ interface RequesterType {
54
+ get<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
55
+ post<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
56
+ put<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
57
+ patch<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
58
+ delete<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
59
+ }
60
+ type RequestHandlerFn<T extends ResponseBodyTypes = ResponseBodyTypes> = (endpoint: string, options?: Record<string, unknown>) => Promise<FormattedResponse<T>>;
61
+ declare function generateRateLimiterFn(limit: number, interval: number): () => Promise<any>;
62
+ declare function formatQuery(params?: Record<string, unknown>): string;
63
+ type OptionsHandlerFn = (serviceOptions: ResourceOptions, requestOptions: RequestOptions) => Promise<RequestOptions>;
64
+ declare function defaultOptionsHandler(resourceOptions: ResourceOptions, { body, searchParams, sudo, signal, asStream, method, }?: DefaultRequestOptions): Promise<RequestOptions>;
65
+ declare function createRateLimiters(rateLimitOptions?: RateLimitOptions, rateLimitDuration?: number): RateLimiters;
66
+ declare function createRequesterFn(optionsHandler: OptionsHandlerFn, requestHandler: RequestHandlerFn): (serviceOptions: ResourceOptions) => RequesterType;
67
+ type PresetConstructors<T> = {
68
+ [K in keyof T]: T[K];
69
+ };
70
+ declare function presetResourceArguments<T extends Record<string, any>>(resources: T, customConfig?: Record<string, unknown>): PresetConstructors<T>;
71
+ declare function getMatchingRateLimiter(endpoint: string, rateLimiters?: RateLimiters, method?: string): RateLimiterFn;
72
+
73
+ interface RootResourceOptions<C> {
74
+ requesterFn?: (resourceOptions: ResourceOptions) => RequesterType;
75
+ host?: string;
76
+ prefixUrl?: string;
77
+ camelize?: C;
78
+ queryTimeout?: number | null;
79
+ rateLimitDuration?: number;
80
+ sudo?: string | number;
81
+ profileToken?: string;
82
+ profileMode?: 'execution' | 'memory';
83
+ rateLimits?: RateLimitOptions;
84
+ agent?: Agent;
85
+ }
86
+ type GitlabToken = string | (() => Promise<string>);
87
+ interface BaseRequestOptionsWithOAuthToken<C> extends RootResourceOptions<C> {
88
+ oauthToken: GitlabToken;
89
+ }
90
+ interface BaseRequestOptionsWithAccessToken<C> extends RootResourceOptions<C> {
91
+ token: GitlabToken;
92
+ }
93
+ interface BaseRequestOptionsWithJobToken<C> extends RootResourceOptions<C> {
94
+ jobToken: GitlabToken;
95
+ }
96
+ type BaseRequestOptionsWithoutToken<C> = RootResourceOptions<C>;
97
+ type BaseResourceOptions<C> = BaseRequestOptionsWithoutToken<C> | BaseRequestOptionsWithOAuthToken<C> | BaseRequestOptionsWithAccessToken<C> | BaseRequestOptionsWithJobToken<C>;
98
+ declare class BaseResource<C extends boolean = false> {
99
+ readonly url: string;
100
+ readonly requester: RequesterType;
101
+ readonly queryTimeout: number | null;
102
+ readonly headers: {
103
+ [header: string]: string;
104
+ };
105
+ readonly authHeaders: {
106
+ [authHeader: string]: () => Promise<string>;
107
+ };
108
+ readonly camelize: C | undefined;
109
+ constructor({ sudo, profileToken, camelize, requesterFn, agent, profileMode, host, prefixUrl, queryTimeout, rateLimitDuration, rateLimits, ...tokens }: BaseResourceOptions<C>);
110
+ }
111
+
112
+ declare class GitbeakerRequestError extends Error {
113
+ readonly cause?: {
114
+ description: string;
115
+ request: Request;
116
+ response: Response;
117
+ };
118
+ constructor(message: string, options?: {
119
+ cause?: {
120
+ description: string;
121
+ request: Request;
122
+ response: Response;
123
+ };
124
+ });
125
+ }
126
+ declare class GitbeakerTimeoutError extends Error {
127
+ constructor(message: string, options?: ErrorOptions);
128
+ }
129
+ declare class GitbeakerRetryError extends Error {
130
+ constructor(message: string, options?: ErrorOptions);
131
+ }
132
+
133
+ export { type BaseRequestOptionsWithAccessToken, type BaseRequestOptionsWithJobToken, type BaseRequestOptionsWithOAuthToken, type BaseRequestOptionsWithoutToken, BaseResource, type BaseResourceOptions, type Constructable, type DefaultRequestOptions, type FormattedResponse, GitbeakerRequestError, GitbeakerRetryError, GitbeakerTimeoutError, type GitlabToken, type OptionsHandlerFn, type RateLimitOptions, type RateLimiterFn, type RateLimiters, type RequestHandlerFn, type RequestOptions, type RequesterType, type ResourceOptions, type ResponseBodyTypes, type RootResourceOptions, createRateLimiters, createRequesterFn, defaultOptionsHandler, formatQuery, generateRateLimiterFn, getMatchingRateLimiter, presetResourceArguments };
@@ -0,0 +1,133 @@
1
+ import { Agent } from 'http';
2
+
3
+ type RateLimiterFn = () => Promise<number>;
4
+ type RateLimiters = Record<string, RateLimiterFn | {
5
+ method: string;
6
+ limit: RateLimiterFn;
7
+ }>;
8
+ type RateLimitOptions = Record<string, number | {
9
+ method: string;
10
+ limit: number;
11
+ }>;
12
+ type ResponseBodyTypes = Record<string, unknown> | Record<string, unknown>[] | ReadableStream | Blob | string | string[] | number | void | null;
13
+ interface FormattedResponse<T extends ResponseBodyTypes = ResponseBodyTypes> {
14
+ body: T;
15
+ headers: Record<string, string>;
16
+ status: number;
17
+ }
18
+ interface Constructable<T = any> {
19
+ new (...args: any[]): T;
20
+ }
21
+ type ResourceOptions = {
22
+ headers: {
23
+ [header: string]: string;
24
+ };
25
+ authHeaders: {
26
+ [authHeader: string]: () => Promise<string>;
27
+ };
28
+ url: string;
29
+ rateLimits?: RateLimitOptions;
30
+ rateLimitDuration?: number;
31
+ agent?: Agent;
32
+ };
33
+ type DefaultRequestOptions = {
34
+ body?: FormData | Record<string, unknown>;
35
+ searchParams?: Record<string, unknown>;
36
+ sudo?: string | number;
37
+ method?: string;
38
+ asStream?: boolean;
39
+ signal?: AbortSignal;
40
+ };
41
+ type RequestOptions = {
42
+ headers?: Record<string, string>;
43
+ timeout?: number;
44
+ method?: string;
45
+ searchParams?: string;
46
+ prefixUrl?: string;
47
+ body?: string | FormData;
48
+ asStream?: boolean;
49
+ signal?: AbortSignal;
50
+ rateLimiters?: Record<string, RateLimiterFn>;
51
+ agent?: Agent;
52
+ };
53
+ interface RequesterType {
54
+ get<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
55
+ post<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
56
+ put<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
57
+ patch<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
58
+ delete<T extends ResponseBodyTypes>(endpoint: string, options?: DefaultRequestOptions): Promise<FormattedResponse<T>>;
59
+ }
60
+ type RequestHandlerFn<T extends ResponseBodyTypes = ResponseBodyTypes> = (endpoint: string, options?: Record<string, unknown>) => Promise<FormattedResponse<T>>;
61
+ declare function generateRateLimiterFn(limit: number, interval: number): () => Promise<any>;
62
+ declare function formatQuery(params?: Record<string, unknown>): string;
63
+ type OptionsHandlerFn = (serviceOptions: ResourceOptions, requestOptions: RequestOptions) => Promise<RequestOptions>;
64
+ declare function defaultOptionsHandler(resourceOptions: ResourceOptions, { body, searchParams, sudo, signal, asStream, method, }?: DefaultRequestOptions): Promise<RequestOptions>;
65
+ declare function createRateLimiters(rateLimitOptions?: RateLimitOptions, rateLimitDuration?: number): RateLimiters;
66
+ declare function createRequesterFn(optionsHandler: OptionsHandlerFn, requestHandler: RequestHandlerFn): (serviceOptions: ResourceOptions) => RequesterType;
67
+ type PresetConstructors<T> = {
68
+ [K in keyof T]: T[K];
69
+ };
70
+ declare function presetResourceArguments<T extends Record<string, any>>(resources: T, customConfig?: Record<string, unknown>): PresetConstructors<T>;
71
+ declare function getMatchingRateLimiter(endpoint: string, rateLimiters?: RateLimiters, method?: string): RateLimiterFn;
72
+
73
+ interface RootResourceOptions<C> {
74
+ requesterFn?: (resourceOptions: ResourceOptions) => RequesterType;
75
+ host?: string;
76
+ prefixUrl?: string;
77
+ camelize?: C;
78
+ queryTimeout?: number | null;
79
+ rateLimitDuration?: number;
80
+ sudo?: string | number;
81
+ profileToken?: string;
82
+ profileMode?: 'execution' | 'memory';
83
+ rateLimits?: RateLimitOptions;
84
+ agent?: Agent;
85
+ }
86
+ type GitlabToken = string | (() => Promise<string>);
87
+ interface BaseRequestOptionsWithOAuthToken<C> extends RootResourceOptions<C> {
88
+ oauthToken: GitlabToken;
89
+ }
90
+ interface BaseRequestOptionsWithAccessToken<C> extends RootResourceOptions<C> {
91
+ token: GitlabToken;
92
+ }
93
+ interface BaseRequestOptionsWithJobToken<C> extends RootResourceOptions<C> {
94
+ jobToken: GitlabToken;
95
+ }
96
+ type BaseRequestOptionsWithoutToken<C> = RootResourceOptions<C>;
97
+ type BaseResourceOptions<C> = BaseRequestOptionsWithoutToken<C> | BaseRequestOptionsWithOAuthToken<C> | BaseRequestOptionsWithAccessToken<C> | BaseRequestOptionsWithJobToken<C>;
98
+ declare class BaseResource<C extends boolean = false> {
99
+ readonly url: string;
100
+ readonly requester: RequesterType;
101
+ readonly queryTimeout: number | null;
102
+ readonly headers: {
103
+ [header: string]: string;
104
+ };
105
+ readonly authHeaders: {
106
+ [authHeader: string]: () => Promise<string>;
107
+ };
108
+ readonly camelize: C | undefined;
109
+ constructor({ sudo, profileToken, camelize, requesterFn, agent, profileMode, host, prefixUrl, queryTimeout, rateLimitDuration, rateLimits, ...tokens }: BaseResourceOptions<C>);
110
+ }
111
+
112
+ declare class GitbeakerRequestError extends Error {
113
+ readonly cause?: {
114
+ description: string;
115
+ request: Request;
116
+ response: Response;
117
+ };
118
+ constructor(message: string, options?: {
119
+ cause?: {
120
+ description: string;
121
+ request: Request;
122
+ response: Response;
123
+ };
124
+ });
125
+ }
126
+ declare class GitbeakerTimeoutError extends Error {
127
+ constructor(message: string, options?: ErrorOptions);
128
+ }
129
+ declare class GitbeakerRetryError extends Error {
130
+ constructor(message: string, options?: ErrorOptions);
131
+ }
132
+
133
+ export { type BaseRequestOptionsWithAccessToken, type BaseRequestOptionsWithJobToken, type BaseRequestOptionsWithOAuthToken, type BaseRequestOptionsWithoutToken, BaseResource, type BaseResourceOptions, type Constructable, type DefaultRequestOptions, type FormattedResponse, GitbeakerRequestError, GitbeakerRetryError, GitbeakerTimeoutError, type GitlabToken, type OptionsHandlerFn, type RateLimitOptions, type RateLimiterFn, type RateLimiters, type RequestHandlerFn, type RequestOptions, type RequesterType, type ResourceOptions, type ResponseBodyTypes, type RootResourceOptions, createRateLimiters, createRequesterFn, defaultOptionsHandler, formatQuery, generateRateLimiterFn, getMatchingRateLimiter, presetResourceArguments };
package/dist/index.js ADDED
@@ -0,0 +1,241 @@
1
+ 'use strict';
2
+
3
+ var qs = require('qs');
4
+ var xcase = require('xcase');
5
+ var rateLimiterFlexible = require('rate-limiter-flexible');
6
+ var Picomatch = require('picomatch-browser');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var Picomatch__default = /*#__PURE__*/_interopDefault(Picomatch);
11
+
12
+ // src/RequesterUtils.ts
13
+ var { isMatch: isGlobMatch } = Picomatch__default.default;
14
+ function generateRateLimiterFn(limit, interval) {
15
+ const limiter = new rateLimiterFlexible.RateLimiterQueue(
16
+ new rateLimiterFlexible.RateLimiterMemory({ points: limit, duration: interval })
17
+ );
18
+ return () => limiter.removeTokens(1);
19
+ }
20
+ function formatQuery(params = {}) {
21
+ const decamelized = xcase.decamelizeKeys(params);
22
+ return qs.stringify(decamelized, { arrayFormat: "brackets" });
23
+ }
24
+ async function defaultOptionsHandler(resourceOptions, {
25
+ body,
26
+ searchParams,
27
+ sudo,
28
+ signal,
29
+ asStream = false,
30
+ method = "GET"
31
+ } = {}) {
32
+ const { headers: preconfiguredHeaders, authHeaders, url, agent } = resourceOptions;
33
+ const defaultOptions = {
34
+ method,
35
+ asStream,
36
+ signal,
37
+ prefixUrl: url,
38
+ agent
39
+ };
40
+ defaultOptions.headers = { ...preconfiguredHeaders };
41
+ if (sudo) defaultOptions.headers.sudo = `${sudo}`;
42
+ if (body) {
43
+ if (body instanceof FormData) {
44
+ defaultOptions.body = body;
45
+ } else {
46
+ defaultOptions.body = JSON.stringify(xcase.decamelizeKeys(body));
47
+ defaultOptions.headers["content-type"] = "application/json";
48
+ }
49
+ }
50
+ if (Object.keys(authHeaders).length > 0) {
51
+ const [authHeaderKey, authHeaderFn] = Object.entries(authHeaders)[0];
52
+ defaultOptions.headers[authHeaderKey] = await authHeaderFn();
53
+ }
54
+ const q = formatQuery(searchParams);
55
+ if (q) defaultOptions.searchParams = q;
56
+ return Promise.resolve(defaultOptions);
57
+ }
58
+ function createRateLimiters(rateLimitOptions = {}, rateLimitDuration = 60) {
59
+ const rateLimiters = {};
60
+ Object.entries(rateLimitOptions).forEach(([key, config]) => {
61
+ if (typeof config === "number")
62
+ rateLimiters[key] = generateRateLimiterFn(config, rateLimitDuration);
63
+ else
64
+ rateLimiters[key] = {
65
+ method: config.method.toUpperCase(),
66
+ limit: generateRateLimiterFn(config.limit, rateLimitDuration)
67
+ };
68
+ });
69
+ return rateLimiters;
70
+ }
71
+ function createRequesterFn(optionsHandler, requestHandler) {
72
+ const methods = ["get", "post", "put", "patch", "delete"];
73
+ return (serviceOptions) => {
74
+ const requester = {};
75
+ const rateLimiters = createRateLimiters(
76
+ serviceOptions.rateLimits,
77
+ serviceOptions.rateLimitDuration
78
+ );
79
+ methods.forEach((m) => {
80
+ requester[m] = async (endpoint, options) => {
81
+ const defaultRequestOptions = await defaultOptionsHandler(serviceOptions, {
82
+ ...options,
83
+ method: m.toUpperCase()
84
+ });
85
+ const requestOptions = await optionsHandler(serviceOptions, defaultRequestOptions);
86
+ return requestHandler(endpoint, { ...requestOptions, rateLimiters });
87
+ };
88
+ });
89
+ return requester;
90
+ };
91
+ }
92
+ function createPresetConstructor(Constructor, presetConfig) {
93
+ return class extends Constructor {
94
+ constructor(...args) {
95
+ const [config, ...rest] = args;
96
+ super({ ...presetConfig, ...config }, ...rest);
97
+ }
98
+ };
99
+ }
100
+ function presetResourceArguments(resources, customConfig = {}) {
101
+ const result = {};
102
+ Object.entries(resources).forEach(([key, Constructor]) => {
103
+ if (typeof Constructor === "function") {
104
+ result[key] = createPresetConstructor(
105
+ Constructor,
106
+ customConfig
107
+ );
108
+ } else {
109
+ result[key] = Constructor;
110
+ }
111
+ });
112
+ return result;
113
+ }
114
+ function getMatchingRateLimiter(endpoint, rateLimiters = {}, method = "GET") {
115
+ const sortedEndpoints = Object.keys(rateLimiters).sort().reverse();
116
+ const match = sortedEndpoints.find((ep) => isGlobMatch(endpoint, ep));
117
+ const rateLimitConfig = match && rateLimiters[match];
118
+ if (typeof rateLimitConfig === "function") return rateLimitConfig;
119
+ if (rateLimitConfig && rateLimitConfig?.method?.toUpperCase() === method.toUpperCase()) {
120
+ return rateLimitConfig.limit;
121
+ }
122
+ return generateRateLimiterFn(3e3, 60);
123
+ }
124
+
125
+ // src/BaseResource.ts
126
+ function getDynamicToken(tokenArgument) {
127
+ return tokenArgument instanceof Function ? tokenArgument() : Promise.resolve(tokenArgument);
128
+ }
129
+ var DEFAULT_RATE_LIMITS = Object.freeze({
130
+ // Default rate limit
131
+ "**": 3e3,
132
+ // Import/Export
133
+ "projects/import": 6,
134
+ "projects/*/export": 6,
135
+ "projects/*/download": 1,
136
+ "groups/import": 6,
137
+ "groups/*/export": 6,
138
+ "groups/*/download": 1,
139
+ // Note creation
140
+ "projects/*/issues/*/notes": {
141
+ method: "post",
142
+ limit: 300
143
+ },
144
+ "projects/*/snippets/*/notes": {
145
+ method: "post",
146
+ limit: 300
147
+ },
148
+ "projects/*/merge_requests/*/notes": {
149
+ method: "post",
150
+ limit: 300
151
+ },
152
+ "groups/*/epics/*/notes": {
153
+ method: "post",
154
+ limit: 300
155
+ },
156
+ // Repositories - get file archive
157
+ "projects/*/repository/archive*": 5,
158
+ // Project Jobs
159
+ "projects/*/jobs": 600,
160
+ // Member deletion
161
+ "projects/*/members": 60,
162
+ "groups/*/members": 60
163
+ });
164
+ var BaseResource = class {
165
+ url;
166
+ requester;
167
+ queryTimeout;
168
+ headers;
169
+ authHeaders;
170
+ camelize;
171
+ constructor({
172
+ sudo,
173
+ profileToken,
174
+ camelize,
175
+ requesterFn,
176
+ agent,
177
+ profileMode = "execution",
178
+ host = "https://gitlab.com",
179
+ prefixUrl = "",
180
+ queryTimeout = 3e5,
181
+ rateLimitDuration = 60,
182
+ rateLimits = DEFAULT_RATE_LIMITS,
183
+ ...tokens
184
+ }) {
185
+ if (!requesterFn) throw new ReferenceError("requesterFn must be passed");
186
+ this.url = [host, "api", "v4", prefixUrl].join("/");
187
+ this.headers = {};
188
+ this.authHeaders = {};
189
+ this.camelize = camelize;
190
+ this.queryTimeout = queryTimeout;
191
+ if ("oauthToken" in tokens)
192
+ this.authHeaders.authorization = async () => {
193
+ const token = await getDynamicToken(tokens.oauthToken);
194
+ return `Bearer ${token}`;
195
+ };
196
+ else if ("jobToken" in tokens)
197
+ this.authHeaders["job-token"] = async () => getDynamicToken(tokens.jobToken);
198
+ else if ("token" in tokens)
199
+ this.authHeaders["private-token"] = async () => getDynamicToken(tokens.token);
200
+ if (profileToken) {
201
+ this.headers["X-Profile-Token"] = profileToken;
202
+ this.headers["X-Profile-Mode"] = profileMode;
203
+ }
204
+ if (sudo) this.headers.Sudo = `${sudo}`;
205
+ this.requester = requesterFn({ ...this, rateLimits, rateLimitDuration, agent });
206
+ }
207
+ };
208
+
209
+ // src/GitbeakerError.ts
210
+ var GitbeakerRequestError = class extends Error {
211
+ cause;
212
+ constructor(message, options) {
213
+ super(message, options);
214
+ this.cause = options?.cause;
215
+ this.name = "GitbeakerRequestError";
216
+ }
217
+ };
218
+ var GitbeakerTimeoutError = class extends Error {
219
+ constructor(message, options) {
220
+ super(message, options);
221
+ this.name = "GitbeakerTimeoutError";
222
+ }
223
+ };
224
+ var GitbeakerRetryError = class extends Error {
225
+ constructor(message, options) {
226
+ super(message, options);
227
+ this.name = "GitbeakerRetryError";
228
+ }
229
+ };
230
+
231
+ exports.BaseResource = BaseResource;
232
+ exports.GitbeakerRequestError = GitbeakerRequestError;
233
+ exports.GitbeakerRetryError = GitbeakerRetryError;
234
+ exports.GitbeakerTimeoutError = GitbeakerTimeoutError;
235
+ exports.createRateLimiters = createRateLimiters;
236
+ exports.createRequesterFn = createRequesterFn;
237
+ exports.defaultOptionsHandler = defaultOptionsHandler;
238
+ exports.formatQuery = formatQuery;
239
+ exports.generateRateLimiterFn = generateRateLimiterFn;
240
+ exports.getMatchingRateLimiter = getMatchingRateLimiter;
241
+ exports.presetResourceArguments = presetResourceArguments;
package/dist/index.mjs ADDED
@@ -0,0 +1,225 @@
1
+ import { stringify } from 'qs';
2
+ import { decamelizeKeys } from 'xcase';
3
+ import { RateLimiterQueue, RateLimiterMemory } from 'rate-limiter-flexible';
4
+ import Picomatch from 'picomatch-browser';
5
+
6
+ // src/RequesterUtils.ts
7
+ var { isMatch: isGlobMatch } = Picomatch;
8
+ function generateRateLimiterFn(limit, interval) {
9
+ const limiter = new RateLimiterQueue(
10
+ new RateLimiterMemory({ points: limit, duration: interval })
11
+ );
12
+ return () => limiter.removeTokens(1);
13
+ }
14
+ function formatQuery(params = {}) {
15
+ const decamelized = decamelizeKeys(params);
16
+ return stringify(decamelized, { arrayFormat: "brackets" });
17
+ }
18
+ async function defaultOptionsHandler(resourceOptions, {
19
+ body,
20
+ searchParams,
21
+ sudo,
22
+ signal,
23
+ asStream = false,
24
+ method = "GET"
25
+ } = {}) {
26
+ const { headers: preconfiguredHeaders, authHeaders, url, agent } = resourceOptions;
27
+ const defaultOptions = {
28
+ method,
29
+ asStream,
30
+ signal,
31
+ prefixUrl: url,
32
+ agent
33
+ };
34
+ defaultOptions.headers = { ...preconfiguredHeaders };
35
+ if (sudo) defaultOptions.headers.sudo = `${sudo}`;
36
+ if (body) {
37
+ if (body instanceof FormData) {
38
+ defaultOptions.body = body;
39
+ } else {
40
+ defaultOptions.body = JSON.stringify(decamelizeKeys(body));
41
+ defaultOptions.headers["content-type"] = "application/json";
42
+ }
43
+ }
44
+ if (Object.keys(authHeaders).length > 0) {
45
+ const [authHeaderKey, authHeaderFn] = Object.entries(authHeaders)[0];
46
+ defaultOptions.headers[authHeaderKey] = await authHeaderFn();
47
+ }
48
+ const q = formatQuery(searchParams);
49
+ if (q) defaultOptions.searchParams = q;
50
+ return Promise.resolve(defaultOptions);
51
+ }
52
+ function createRateLimiters(rateLimitOptions = {}, rateLimitDuration = 60) {
53
+ const rateLimiters = {};
54
+ Object.entries(rateLimitOptions).forEach(([key, config]) => {
55
+ if (typeof config === "number")
56
+ rateLimiters[key] = generateRateLimiterFn(config, rateLimitDuration);
57
+ else
58
+ rateLimiters[key] = {
59
+ method: config.method.toUpperCase(),
60
+ limit: generateRateLimiterFn(config.limit, rateLimitDuration)
61
+ };
62
+ });
63
+ return rateLimiters;
64
+ }
65
+ function createRequesterFn(optionsHandler, requestHandler) {
66
+ const methods = ["get", "post", "put", "patch", "delete"];
67
+ return (serviceOptions) => {
68
+ const requester = {};
69
+ const rateLimiters = createRateLimiters(
70
+ serviceOptions.rateLimits,
71
+ serviceOptions.rateLimitDuration
72
+ );
73
+ methods.forEach((m) => {
74
+ requester[m] = async (endpoint, options) => {
75
+ const defaultRequestOptions = await defaultOptionsHandler(serviceOptions, {
76
+ ...options,
77
+ method: m.toUpperCase()
78
+ });
79
+ const requestOptions = await optionsHandler(serviceOptions, defaultRequestOptions);
80
+ return requestHandler(endpoint, { ...requestOptions, rateLimiters });
81
+ };
82
+ });
83
+ return requester;
84
+ };
85
+ }
86
+ function createPresetConstructor(Constructor, presetConfig) {
87
+ return class extends Constructor {
88
+ constructor(...args) {
89
+ const [config, ...rest] = args;
90
+ super({ ...presetConfig, ...config }, ...rest);
91
+ }
92
+ };
93
+ }
94
+ function presetResourceArguments(resources, customConfig = {}) {
95
+ const result = {};
96
+ Object.entries(resources).forEach(([key, Constructor]) => {
97
+ if (typeof Constructor === "function") {
98
+ result[key] = createPresetConstructor(
99
+ Constructor,
100
+ customConfig
101
+ );
102
+ } else {
103
+ result[key] = Constructor;
104
+ }
105
+ });
106
+ return result;
107
+ }
108
+ function getMatchingRateLimiter(endpoint, rateLimiters = {}, method = "GET") {
109
+ const sortedEndpoints = Object.keys(rateLimiters).sort().reverse();
110
+ const match = sortedEndpoints.find((ep) => isGlobMatch(endpoint, ep));
111
+ const rateLimitConfig = match && rateLimiters[match];
112
+ if (typeof rateLimitConfig === "function") return rateLimitConfig;
113
+ if (rateLimitConfig && rateLimitConfig?.method?.toUpperCase() === method.toUpperCase()) {
114
+ return rateLimitConfig.limit;
115
+ }
116
+ return generateRateLimiterFn(3e3, 60);
117
+ }
118
+
119
+ // src/BaseResource.ts
120
+ function getDynamicToken(tokenArgument) {
121
+ return tokenArgument instanceof Function ? tokenArgument() : Promise.resolve(tokenArgument);
122
+ }
123
+ var DEFAULT_RATE_LIMITS = Object.freeze({
124
+ // Default rate limit
125
+ "**": 3e3,
126
+ // Import/Export
127
+ "projects/import": 6,
128
+ "projects/*/export": 6,
129
+ "projects/*/download": 1,
130
+ "groups/import": 6,
131
+ "groups/*/export": 6,
132
+ "groups/*/download": 1,
133
+ // Note creation
134
+ "projects/*/issues/*/notes": {
135
+ method: "post",
136
+ limit: 300
137
+ },
138
+ "projects/*/snippets/*/notes": {
139
+ method: "post",
140
+ limit: 300
141
+ },
142
+ "projects/*/merge_requests/*/notes": {
143
+ method: "post",
144
+ limit: 300
145
+ },
146
+ "groups/*/epics/*/notes": {
147
+ method: "post",
148
+ limit: 300
149
+ },
150
+ // Repositories - get file archive
151
+ "projects/*/repository/archive*": 5,
152
+ // Project Jobs
153
+ "projects/*/jobs": 600,
154
+ // Member deletion
155
+ "projects/*/members": 60,
156
+ "groups/*/members": 60
157
+ });
158
+ var BaseResource = class {
159
+ url;
160
+ requester;
161
+ queryTimeout;
162
+ headers;
163
+ authHeaders;
164
+ camelize;
165
+ constructor({
166
+ sudo,
167
+ profileToken,
168
+ camelize,
169
+ requesterFn,
170
+ agent,
171
+ profileMode = "execution",
172
+ host = "https://gitlab.com",
173
+ prefixUrl = "",
174
+ queryTimeout = 3e5,
175
+ rateLimitDuration = 60,
176
+ rateLimits = DEFAULT_RATE_LIMITS,
177
+ ...tokens
178
+ }) {
179
+ if (!requesterFn) throw new ReferenceError("requesterFn must be passed");
180
+ this.url = [host, "api", "v4", prefixUrl].join("/");
181
+ this.headers = {};
182
+ this.authHeaders = {};
183
+ this.camelize = camelize;
184
+ this.queryTimeout = queryTimeout;
185
+ if ("oauthToken" in tokens)
186
+ this.authHeaders.authorization = async () => {
187
+ const token = await getDynamicToken(tokens.oauthToken);
188
+ return `Bearer ${token}`;
189
+ };
190
+ else if ("jobToken" in tokens)
191
+ this.authHeaders["job-token"] = async () => getDynamicToken(tokens.jobToken);
192
+ else if ("token" in tokens)
193
+ this.authHeaders["private-token"] = async () => getDynamicToken(tokens.token);
194
+ if (profileToken) {
195
+ this.headers["X-Profile-Token"] = profileToken;
196
+ this.headers["X-Profile-Mode"] = profileMode;
197
+ }
198
+ if (sudo) this.headers.Sudo = `${sudo}`;
199
+ this.requester = requesterFn({ ...this, rateLimits, rateLimitDuration, agent });
200
+ }
201
+ };
202
+
203
+ // src/GitbeakerError.ts
204
+ var GitbeakerRequestError = class extends Error {
205
+ cause;
206
+ constructor(message, options) {
207
+ super(message, options);
208
+ this.cause = options?.cause;
209
+ this.name = "GitbeakerRequestError";
210
+ }
211
+ };
212
+ var GitbeakerTimeoutError = class extends Error {
213
+ constructor(message, options) {
214
+ super(message, options);
215
+ this.name = "GitbeakerTimeoutError";
216
+ }
217
+ };
218
+ var GitbeakerRetryError = class extends Error {
219
+ constructor(message, options) {
220
+ super(message, options);
221
+ this.name = "GitbeakerRetryError";
222
+ }
223
+ };
224
+
225
+ export { BaseResource, GitbeakerRequestError, GitbeakerRetryError, GitbeakerTimeoutError, createRateLimiters, createRequesterFn, defaultOptionsHandler, formatQuery, generateRateLimiterFn, getMatchingRateLimiter, presetResourceArguments };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@gitbeaker/requester-utils",
3
+ "version": "0.0.0-canary-20260118165457",
4
+ "description": "Utility functions for requester implementatons used in @gitbeaker",
5
+ "license": "MIT",
6
+ "engines": {
7
+ "node": ">=18.20.0"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/jdalrymple/gitbeaker",
12
+ "directory": "packages/requester-utils"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/jdalrymple/gitbeaker/issues"
16
+ },
17
+ "homepage": "https://github.com/jdalrymple/gitbeaker#readme",
18
+ "author": {
19
+ "name": "Justin Dalrymple"
20
+ },
21
+ "keywords": [
22
+ "gitlab",
23
+ "utils",
24
+ "gitbeaker"
25
+ ],
26
+ "types": "./dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "import": {
30
+ "types": "./dist/index.d.mts",
31
+ "default": "./dist/index.mjs"
32
+ },
33
+ "require": {
34
+ "types": "./dist/index.d.ts",
35
+ "default": "./dist/index.js"
36
+ }
37
+ }
38
+ },
39
+ "files": [
40
+ "dist"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsup src/index.ts --format esm,cjs --dts --treeshake",
44
+ "test:types": "tsc",
45
+ "test:unit": "jest --maxWorkers=50% test/unit",
46
+ "lint": "eslint '**/{src,test,scripts}/**/*.ts'",
47
+ "lint:fix": "yarn lint --fix"
48
+ },
49
+ "dependencies": {
50
+ "picomatch-browser": "^2.2.6",
51
+ "qs": "^6.14.0",
52
+ "rate-limiter-flexible": "^8.2.1",
53
+ "xcase": "^2.0.1"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^24.10.1",
57
+ "@types/qs": "^6.14.0",
58
+ "tsup": "^8.5.1",
59
+ "typescript": "^5.9.3"
60
+ }
61
+ }